From 1ea5a15d1edb63dab09bbc1543ca912dfed3ca91 Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud <7252775+indietyp@users.noreply.github.com> Date: Fri, 19 Jun 2026 11:36:06 +0200 Subject: [PATCH 01/12] chore: checkpoint --- libs/@local/hashql/core/Cargo.toml | 4 ++ libs/@local/hashql/core/benches/stdlib.rs | 65 +++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 libs/@local/hashql/core/benches/stdlib.rs diff --git a/libs/@local/hashql/core/Cargo.toml b/libs/@local/hashql/core/Cargo.toml index 8a6b5b68221..e51611ca379 100644 --- a/libs/@local/hashql/core/Cargo.toml +++ b/libs/@local/hashql/core/Cargo.toml @@ -64,3 +64,7 @@ harness = false [[bench]] name = "bit_matrix" harness = false + +[[bench]] +name = "stdlib" +harness = false diff --git a/libs/@local/hashql/core/benches/stdlib.rs b/libs/@local/hashql/core/benches/stdlib.rs new file mode 100644 index 00000000000..43fa9a17385 --- /dev/null +++ b/libs/@local/hashql/core/benches/stdlib.rs @@ -0,0 +1,65 @@ +#![expect(clippy::significant_drop_tightening)] + +use core::hint::black_box; +use std::time::Duration; + +use codspeed_criterion_compat::{BatchSize, Criterion, criterion_group, criterion_main}; +use darwin_kperf_criterion::HardwareCounter; +use hashql_core::{ + heap::{Heap, ResetAllocator as _}, + module::ModuleRegistry, + r#type::environment::Environment, +}; + +fn stdlib_construction(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("stdlib"); + + group.bench_function("construction", |bencher| { + let mut heap = Heap::new(); + let heap_ptr = &raw mut heap; + + // IMPORTANT: `BatchSize::PerIteration` is critical for soundness. Do NOT change this to + // `SmallInput`, `LargeInput`, or any other batch size. Doing so will cause undefined + // behavior (use-after-free of arena allocations). + bencher.iter_batched( + || { + // SAFETY: We create a `&mut Heap` from the raw pointer to call `reset()` and + // build the environment. This is sound because: + // - `heap` outlives the entire `iter_batched` call (it's a local in the outer + // scope). + // - `BatchSize::PerIteration` ensures only one environment exists at a time, + // dropped before the next setup call. + // - No other references to `heap` exist during this closure's execution. + // - This code runs single-threaded. + #[expect(unsafe_code)] + let heap = unsafe { &mut *heap_ptr }; + heap.reset(); + + Environment::new(heap) + }, + |environment| { + black_box(ModuleRegistry::new(&environment)); + }, + BatchSize::PerIteration, + ); + }); + + group.finish(); +} + +fn measurement() -> Criterion { + Criterion::default() + .with_measurement( + HardwareCounter::instructions().expect("failed to initialize hardware counters"), + ) + .warm_up_time(Duration::from_millis(500)) + .measurement_time(Duration::from_secs(3)) + .sample_size(50) +} + +criterion_group! { + name = benches; + config = measurement(); + targets = stdlib_construction +} +criterion_main!(benches); From e229de5626433be6b19aa90b855289f4a30babb6 Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud <7252775+indietyp@users.noreply.github.com> Date: Fri, 19 Jun 2026 12:19:49 +0200 Subject: [PATCH 02/12] feat: replace dynamic symbols with real ones --- libs/@local/hashql/core/benches/stdlib.rs | 53 +++++++++++++++---- .../core/src/module/std_lib/core/json.rs | 5 +- .../core/src/module/std_lib/core/result.rs | 20 +++---- .../core/src/module/std_lib/graph/body.rs | 3 +- .../core/src/module/std_lib/graph/entity.rs | 7 ++- .../core/src/module/std_lib/graph/head.rs | 5 +- .../core/src/module/std_lib/graph/mod.rs | 10 ++-- .../core/src/module/std_lib/graph/tail.rs | 3 +- .../graph/types/ontology/entity_type.rs | 19 ++++--- .../hashql/core/src/module/std_lib/mod.rs | 3 -- libs/@local/hashql/core/src/symbol/sym.rs | 21 +++++++- 11 files changed, 95 insertions(+), 54 deletions(-) diff --git a/libs/@local/hashql/core/benches/stdlib.rs b/libs/@local/hashql/core/benches/stdlib.rs index 43fa9a17385..7070e4f1ece 100644 --- a/libs/@local/hashql/core/benches/stdlib.rs +++ b/libs/@local/hashql/core/benches/stdlib.rs @@ -3,7 +3,9 @@ use core::hint::black_box; use std::time::Duration; -use codspeed_criterion_compat::{BatchSize, Criterion, criterion_group, criterion_main}; +use codspeed_criterion_compat::{ + BatchSize, Criterion, criterion_group, criterion_main, measurement::Measurement, +}; use darwin_kperf_criterion::HardwareCounter; use hashql_core::{ heap::{Heap, ResetAllocator as _}, @@ -11,7 +13,7 @@ use hashql_core::{ r#type::environment::Environment, }; -fn stdlib_construction(criterion: &mut Criterion) { +fn stdlib_construction(criterion: &mut Criterion) { let mut group = criterion.benchmark_group("stdlib"); group.bench_function("construction", |bencher| { @@ -47,19 +49,52 @@ fn stdlib_construction(criterion: &mut Criterion) { group.finish(); } -fn measurement() -> Criterion { +fn measurement(counter: HardwareCounter) -> Criterion { Criterion::default() - .with_measurement( - HardwareCounter::instructions().expect("failed to initialize hardware counters"), - ) + .with_measurement(counter) .warm_up_time(Duration::from_millis(500)) .measurement_time(Duration::from_secs(3)) .sample_size(50) } +fn measurement_instructions() -> Criterion { + measurement(HardwareCounter::instructions().expect("failed to initialize hardware counters")) +} + +fn measurement_l1d_cache_misses() -> Criterion { + measurement( + HardwareCounter::l1d_cache_misses().expect("failed to initialize hardware counters"), + ) +} + +fn measurement_branch_misses() -> Criterion { + measurement( + HardwareCounter::branch_mispredictions().expect("failed to initialize hardware counters"), + ) +} + criterion_group! { - name = benches; - config = measurement(); + name = walltime; + config = Criterion::default(); targets = stdlib_construction } -criterion_main!(benches); + +criterion_group!( + name = instructions; + config = measurement_instructions(); + targets = stdlib_construction +); + +criterion_group!( + name = l1d_cache_misses; + config = measurement_l1d_cache_misses(); + targets = stdlib_construction +); + +criterion_group!( + name = branch_misses; + config = measurement_branch_misses(); + targets = stdlib_construction +); + +criterion_main!(walltime, instructions, l1d_cache_misses, branch_misses); diff --git a/libs/@local/hashql/core/src/module/std_lib/core/json.rs b/libs/@local/hashql/core/src/module/std_lib/core/json.rs index 17383585dcd..46dcbd0bd9f 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/json.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/json.rs @@ -16,20 +16,19 @@ impl<'heap> StandardLibraryModule<'heap> for Json { fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { let mut def = ModuleDef::new(); - let heap = lib.heap; // type JsonPathSegment = String | Integer; // Note: The type should be Natural instead, but this requires refinement types let json_path_segment_ty = lib.ty.union([lib.ty.string(), lib.ty.integer()]); def.push( - heap.intern_symbol("JsonPathSegment"), + sym::JsonPathSegment, ItemDef::r#type(lib.ty.env, json_path_segment_ty, &[]), ); // type JsonPath = JsonPathSegment[]; let json_path_ty = lib.ty.list(json_path_segment_ty); def.push( - heap.intern_symbol("JsonPath"), + sym::JsonPath, ItemDef::r#type(lib.ty.env, json_path_ty, &[]), ); diff --git a/libs/@local/hashql/core/src/module/std_lib/core/result.rs b/libs/@local/hashql/core/src/module/std_lib/core/result.rs index 91018546d28..1b9237abf1e 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/result.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/result.rs @@ -17,38 +17,32 @@ impl<'heap> StandardLibraryModule<'heap> for Result { fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { let mut def = ModuleDef::new(); - let t_arg = lib.ty.fresh_argument("T"); + let t_arg = lib.ty.fresh_argument(sym::T); let t_ref = lib.ty.hydrate_argument(t_arg); let t_param = lib.ty.param(t_arg); - let e_arg = lib.ty.fresh_argument("E"); + let e_arg = lib.ty.fresh_argument(sym::E); let e_ref = lib.ty.hydrate_argument(e_arg); let e_param = lib.ty.param(e_arg); // newtype Ok = T; let ok_ty = lib.ty.generic( [(t_arg, None)], - lib.ty.opaque("::core::result::Ok", t_param), - ); - def.push( - lib.heap.intern_symbol("Ok"), - ItemDef::newtype(lib.ty.env, ok_ty, &[t_ref]), + lib.ty.opaque(sym::path::core::result::Ok, t_param), ); + def.push(sym::Ok, ItemDef::newtype(lib.ty.env, ok_ty, &[t_ref])); // newtype Err = E; let err_ty = lib.ty.generic( [(e_arg, None)], - lib.ty.opaque("::core::result::Err", e_param), - ); - def.push( - lib.heap.intern_symbol("Err"), - ItemDef::newtype(lib.ty.env, err_ty, &[e_ref]), + lib.ty.opaque(sym::path::core::result::Err, e_param), ); + def.push(sym::Err, ItemDef::newtype(lib.ty.env, err_ty, &[e_ref])); // type Result = Ok | Err; let result_ty = lib.ty.union([ok_ty, err_ty]); def.push( - lib.heap.intern_symbol("Result"), + sym::Result, ItemDef::newtype(lib.ty.env, result_ty, &[t_ref, e_ref]), ); diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/body.rs b/libs/@local/hashql/core/src/module/std_lib/graph/body.rs index d2db4de2838..a49263997bc 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/body.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/body.rs @@ -20,11 +20,10 @@ impl<'heap> StandardLibraryModule<'heap> for Body { fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { let mut def = ModuleDef::new(); - let heap = lib.heap; let graph = lib.manifest::(); - let mut graph_param = graph.expect_type(heap.intern_symbol("Graph")); + let mut graph_param = graph.expect_type(sym::Graph); let mut graph_returns = graph_param; graph_param.instantiate(&mut lib.instantiate); diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/entity.rs b/libs/@local/hashql/core/src/module/std_lib/graph/entity.rs index 5567dae0173..461e4125633 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/entity.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/entity.rs @@ -27,20 +27,19 @@ impl<'heap> StandardLibraryModule<'heap> for Entity { fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { let mut def = ModuleDef::new(); - let heap = lib.heap; let mut entity_ty = lib .manifest::() - .expect_newtype(heap.intern_symbol("Entity")); + .expect_newtype(sym::Entity); entity_ty.instantiate(&mut lib.instantiate); let versioned_url_ty = lib .manifest::() - .expect_newtype(heap.intern_symbol("VersionedUrl")); + .expect_newtype(sym::VersionedUrl); let json_path_ty = lib .manifest::() - .expect_type(heap.intern_symbol("JsonPath")); + .expect_type(sym::JsonPath); // `is_of_type(entity: Entity, depth: Integer, type: VersionedUrl) -> Boolean` // see: https://linear.app/hash/issue/H-4741/hashql-support-for-type-guards diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/head.rs b/libs/@local/hashql/core/src/module/std_lib/graph/head.rs index 5489040d803..dd983bfe8b3 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/head.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/head.rs @@ -24,7 +24,6 @@ impl<'heap> StandardLibraryModule<'heap> for Head { fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { let mut def = ModuleDef::new(); - let heap = lib.heap; let query_temporal_axes_ty = lib .manifest::() @@ -32,12 +31,12 @@ impl<'heap> StandardLibraryModule<'heap> for Head { let mut graph_ty = lib .manifest::() - .expect_type(heap.intern_symbol("Graph")); + .expect_type(sym::Graph); graph_ty.instantiate(&mut lib.instantiate); let mut entity = lib .manifest::() - .expect_newtype(heap.intern_symbol("Entity")); + .expect_newtype(sym::Entity); entity.instantiate(&mut lib.instantiate); // ::graph::head::entities(axis: TimeAxis) -> Graph>; diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/mod.rs b/libs/@local/hashql/core/src/module/std_lib/graph/mod.rs index ea6bc9ae423..fd09dd9b5c0 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/mod.rs @@ -42,17 +42,19 @@ impl<'heap> StandardLibraryModule<'heap> for Graph { // internally to track the graph's type information. The field is named `'marker` using an // identifier that cannot be referenced in user code (`'` is not a valid symbol in any // identifier). - let t_arg = lib.ty.fresh_argument("T"); + let t_arg = lib.ty.fresh_argument(sym::T); let t_ref = lib.ty.hydrate_argument(t_arg); let t_param = lib.ty.param(t_arg); let graph_ty = lib.ty.generic( [t_arg], - lib.ty - .opaque("::graph::Graph", lib.ty.r#struct([("'marker", t_param)])), + lib.ty.opaque( + sym::path::graph::Graph, + lib.ty.r#struct([(sym::internal::marker, t_param)]), + ), ); def.push( - lib.heap.intern_symbol("Graph"), + sym::Graph, // Export as `type` rather than `newtype` since Graph is not intended to be // user-constructible ItemDef::r#type(lib.ty.env, graph_ty, &[t_ref]), diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/tail.rs b/libs/@local/hashql/core/src/module/std_lib/graph/tail.rs index daf05eb5d9d..5cf3e2ec91e 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/tail.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/tail.rs @@ -20,11 +20,10 @@ impl<'heap> StandardLibraryModule<'heap> for Tail { fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { let mut def = ModuleDef::new(); - let heap = lib.heap; let graph = lib.manifest::(); - let mut graph_ty = graph.expect_type(heap.intern_symbol("Graph")); + let mut graph_ty = graph.expect_type(sym::Graph); graph_ty.instantiate(&mut lib.instantiate); // `collect(graph: Graph) -> List;` diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/entity_type.rs b/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/entity_type.rs index d7187cd2ebc..03346c804c3 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/entity_type.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/entity_type.rs @@ -22,34 +22,33 @@ impl<'heap> StandardLibraryModule<'heap> for EntityType { fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { let mut def = ModuleDef::new(); - let heap = lib.heap; // newtype EntityTypeMetadata = (web_id: Option) let web_id = lib .manifest::() - .expect_newtype(heap.intern_symbol("WebId")); + .expect_newtype(sym::WebId); let entity_type_metadata_ty = lib.ty.opaque( - "::graph::types::ontology::entity_type::EntityTypeMetadata", - lib.ty.r#struct([("web_id", option(&lib.ty, web_id.id))]), + sym::path::graph::types::ontology::entity_type::EntityTypeMetadata, + lib.ty.r#struct([(sym::web_id, option(&lib.ty, web_id.id))]), ); def.push( - heap.intern_symbol("EntityTypeMetadata"), + sym::EntityTypeMetadata, ItemDef::newtype(lib.ty.env, entity_type_metadata_ty, &[]), ); // newtype EntityType = (id: VersionedUrl, metadata: EntityTypeMetadata) let versioned_url = lib .manifest::() - .expect_newtype(heap.intern_symbol("VersionedUrl")); + .expect_newtype(sym::VersionedUrl); let entity_id_ty = lib.ty.opaque( - "::graph::types::ontology::entity_type::EntityType", + sym::path::graph::types::ontology::entity_type::EntityType, lib.ty.r#struct([ - ("id", versioned_url.id), - ("metadata", entity_type_metadata_ty), + (sym::id, versioned_url.id), + (sym::metadata, entity_type_metadata_ty), ]), ); def.push( - heap.intern_symbol("EntityType"), + sym::EntityType, ItemDef::newtype(lib.ty.env, entity_id_ty, &[]), ); diff --git a/libs/@local/hashql/core/src/module/std_lib/mod.rs b/libs/@local/hashql/core/src/module/std_lib/mod.rs index d6631f3213c..94733b6ac04 100644 --- a/libs/@local/hashql/core/src/module/std_lib/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/mod.rs @@ -7,7 +7,6 @@ use ::core::{iter, num::NonZero}; use super::{ModuleId, ModuleRegistry, item::IntrinsicItem, locals::TypeDef}; use crate::{ collections::SmallVec, - heap::Heap, module::{ PartialModule, item::{ConstructorItem, Item, ItemKind}, @@ -138,7 +137,6 @@ impl<'heap> ModuleDef<'heap> { } pub(super) struct StandardLibrary<'env, 'heap> { - heap: &'heap Heap, instantiate: InstantiateEnvironment<'env, 'heap>, registry: &'env ModuleRegistry<'heap>, ty: TypeBuilder<'env, 'heap>, @@ -151,7 +149,6 @@ impl<'env, 'heap> StandardLibrary<'env, 'heap> { registry: &'env ModuleRegistry<'heap>, ) -> Self { Self { - heap: environment.heap, instantiate: InstantiateEnvironment::new(environment), registry, ty: TypeBuilder::synthetic(environment), diff --git a/libs/@local/hashql/core/src/symbol/sym.rs b/libs/@local/hashql/core/src/symbol/sym.rs index 965711af38f..8fd7272a427 100644 --- a/libs/@local/hashql/core/src/symbol/sym.rs +++ b/libs/@local/hashql/core/src/symbol/sym.rs @@ -50,6 +50,8 @@ hashql_macros::define_symbols! { end, entity, Entity, + EntityType, + EntityTypeMetadata, entity_edition_id, entity_id, entity_type_ids, @@ -189,6 +191,7 @@ hashql_macros::define_symbols! { body, tmp, decision_time_now, + Graph, graph, types, principal, @@ -200,10 +203,13 @@ hashql_macros::define_symbols! { url, result, json, + JsonPath, + JsonPathSegment, // [tidy] sort alphabetically end internal: { - ClosureEnv: "'" + ClosureEnv: "'", + marker: "'marker" }, symbol: { @@ -351,15 +357,28 @@ hashql_macros::define_symbols! { sqrt: "::core::math::sqrt", cbrt: "::core::math::cbrt", root: "::core::math::root", + }, + result: { + Ok: "::core::result::Ok", + Err: "::core::result::Err", } }, graph: { + Graph: "::graph::Graph", entity: { is_of_type: "::graph::entity::is_of_type", property: "::graph::entity::property", }, tmp: { decision_time_now: "::graph::tmp::decision_time_now", + }, + types: { + ontology: { + entity_type: { + EntityType: "::graph::types::ontology::entity_type::EntityType", + EntityTypeMetadata: "::graph::types::ontology::entity_type::EntityTypeMetadata", + } + } } } // [tidy] sort alphabetically end From 80b6eb69bd6b2afe6e39d036dbdca9afab100d78 Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud <7252775+indietyp@users.noreply.github.com> Date: Fri, 19 Jun 2026 12:50:54 +0200 Subject: [PATCH 03/12] feat: module cache --- libs/@local/hashql/core/benches/stdlib.rs | 3 +- .../hashql/core/src/module/std_lib/mod.rs | 104 ++++++++++++++++-- 2 files changed, 96 insertions(+), 11 deletions(-) diff --git a/libs/@local/hashql/core/benches/stdlib.rs b/libs/@local/hashql/core/benches/stdlib.rs index 7070e4f1ece..6f60ece0abc 100644 --- a/libs/@local/hashql/core/benches/stdlib.rs +++ b/libs/@local/hashql/core/benches/stdlib.rs @@ -1,7 +1,6 @@ #![expect(clippy::significant_drop_tightening)] -use core::hint::black_box; -use std::time::Duration; +use core::{hint::black_box, time::Duration}; use codspeed_criterion_compat::{ BatchSize, Criterion, criterion_group, criterion_main, measurement::Measurement, diff --git a/libs/@local/hashql/core/src/module/std_lib/mod.rs b/libs/@local/hashql/core/src/module/std_lib/mod.rs index 94733b6ac04..5d126044526 100644 --- a/libs/@local/hashql/core/src/module/std_lib/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/mod.rs @@ -2,16 +2,19 @@ pub mod core; pub mod graph; mod kernel; -use ::core::{iter, num::NonZero}; +use std::alloc::Allocator; + +use ::core::{iter, mem::MaybeUninit, num::NonZero}; use super::{ModuleId, ModuleRegistry, item::IntrinsicItem, locals::TypeDef}; use crate::{ collections::SmallVec, + id::{Id, bit_vec::DenseBitSet}, module::{ PartialModule, item::{ConstructorItem, Item, ItemKind}, }, - symbol::Symbol, + symbol::{Symbol, sym::newtype}, r#type::{ TypeBuilder, TypeId, environment::{Environment, instantiate::InstantiateEnvironment}, @@ -73,12 +76,11 @@ impl<'heap> ModuleEntry<'heap> { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -struct ModuleDef<'heap>(SmallVec>); +struct ModuleDef<'heap, S: Allocator>(Vec, S>); -impl<'heap> ModuleDef<'heap> { - const fn new() -> Self { - Self(SmallVec::new()) +impl<'heap, S: Allocator> ModuleDef<'heap, S> { + const fn new_in(alloc: S) -> Self { + Self(Vec::new_in(alloc)) } fn push(&mut self, name: Symbol<'heap>, def: ItemDef<'heap>) -> usize { @@ -136,11 +138,95 @@ impl<'heap> ModuleDef<'heap> { } } -pub(super) struct StandardLibrary<'env, 'heap> { +hashql_macros::define_id! { + #[id(crate = crate)] + struct CacheId(u8 is 0..=u8::MAX) +} + +struct ModuleCache<'heap, S: Allocator> { + entries: Option>], S>>, + occupied: DenseBitSet, +} + +#[expect(unsafe_code)] +impl<'heap, S: Allocator> ModuleCache<'heap, S> { + fn new(length: usize, alloc: S) -> Self { + Self { + entries: Some(Box::new_uninit_slice_in(length, alloc)), + occupied: DenseBitSet::new_empty(length), + } + } + + fn take(&mut self) -> Option], S>> { + let Some(entries) = self.entries.take() else { + panic!("cache has not been initialized") + }; + + if self.occupied.count() != entries.len() { + self.entries = Some(entries); + return None; + } + + // SAFETY: we know the entry is initialized because we checked `occupied` above + Some(unsafe { entries.assume_init() }) + } + + fn insert(&mut self, id: CacheId, module: ModuleDef<'heap, S>) -> bool { + let Some(entries) = &mut self.entries else { + panic!("cache has not been initialized") + }; + + if self.occupied.contains(id) { + return false; + } + + entries[id.as_usize()].write(module); + + // We only insert AFTER the entry has been initialized because otherwise we could drop a + // value on panic that hasn't been initialized. + self.occupied.insert(id); + true + } + + fn get(&self, id: CacheId) -> Option<&ModuleDef<'heap, S>> { + let Some(entries) = &self.entries else { + return None; + }; + + if !self.occupied.contains(id) { + return None; + } + + // SAFETY: we know the entry is initialized because we checked `occupied` above + Some(unsafe { entries[id.as_usize()].assume_init_ref() }) + } +} + +#[expect(unsafe_code)] +impl<'heap, S: Allocator> Drop for ModuleCache<'heap, S> { + fn drop(&mut self) { + let Some(mut entries) = self.entries.take() else { + // entries have already been taken from the cache + return; + }; + + if self.occupied.count() == entries.len() { + // SAFETY: all the elements have been initialized + unsafe { entries.assume_init_drop() }; + } else { + for index in &self.occupied { + // SAFETY: the element has been initialized by the cache + unsafe { entries[index.as_usize()].assume_init_drop() }; + } + } + } +} + +pub(super) struct StandardLibrary<'env, 'heap, S: Allocator> { instantiate: InstantiateEnvironment<'env, 'heap>, registry: &'env ModuleRegistry<'heap>, ty: TypeBuilder<'env, 'heap>, - modules: SmallVec<(::core::any::TypeId, ModuleDef<'heap>)>, + modules: ModuleCache<'heap, S>, } impl<'env, 'heap> StandardLibrary<'env, 'heap> { From 716eec8599ff64c2ff877081b30ba29f18d9eff5 Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud <7252775+indietyp@users.noreply.github.com> Date: Fri, 19 Jun 2026 13:59:39 +0200 Subject: [PATCH 04/12] feat: stdlib reorganization --- libs/@local/hashql/core/benches/stdlib.rs | 2 +- libs/@local/hashql/core/src/lib.rs | 3 +- libs/@local/hashql/core/src/module/mod.rs | 16 +- .../core/src/module/std_lib/core/bits.rs | 25 +- .../core/src/module/std_lib/core/bool.rs | 19 +- .../core/src/module/std_lib/core/cmp.rs | 27 +- .../core/src/module/std_lib/core/json.rs | 23 +- .../core/src/module/std_lib/core/math.rs | 35 ++- .../core/src/module/std_lib/core/mod.rs | 15 +- .../core/src/module/std_lib/core/option.rs | 36 ++- .../core/src/module/std_lib/core/result.rs | 41 +-- .../core/src/module/std_lib/core/url.rs | 17 +- .../core/src/module/std_lib/core/uuid.rs | 15 +- .../core/src/module/std_lib/graph/body.rs | 29 +- .../core/src/module/std_lib/graph/entity.rs | 40 +-- .../core/src/module/std_lib/graph/head.rs | 40 ++- .../core/src/module/std_lib/graph/mod.rs | 38 +-- .../core/src/module/std_lib/graph/tail.rs | 23 +- .../core/src/module/std_lib/graph/temporal.rs | 124 ++++---- .../core/src/module/std_lib/graph/tmp.rs | 24 +- .../std_lib/graph/types/knowledge/entity.rs | 46 +-- .../std_lib/graph/types/knowledge/mod.rs | 14 +- .../src/module/std_lib/graph/types/mod.rs | 14 +- .../graph/types/ontology/entity_type.rs | 37 ++- .../std_lib/graph/types/ontology/mod.rs | 37 ++- .../graph/types/principal/actor_group/mod.rs | 22 +- .../graph/types/principal/actor_group/web.rs | 22 +- .../std_lib/graph/types/principal/mod.rs | 14 +- .../core/src/module/std_lib/kernel/mod.rs | 11 +- .../src/module/std_lib/kernel/special_form.rs | 41 +-- .../core/src/module/std_lib/kernel/type.rs | 37 ++- .../hashql/core/src/module/std_lib/mod.rs | 284 ++++++++++-------- 32 files changed, 693 insertions(+), 478 deletions(-) diff --git a/libs/@local/hashql/core/benches/stdlib.rs b/libs/@local/hashql/core/benches/stdlib.rs index 6f60ece0abc..90a8704dc93 100644 --- a/libs/@local/hashql/core/benches/stdlib.rs +++ b/libs/@local/hashql/core/benches/stdlib.rs @@ -39,7 +39,7 @@ fn stdlib_construction(criterion: &mut Criterion) { Environment::new(heap) }, |environment| { - black_box(ModuleRegistry::new(&environment)); + black_box(ModuleRegistry::new_in(&environment, environment.heap)); }, BatchSize::PerIteration, ); diff --git a/libs/@local/hashql/core/src/lib.rs b/libs/@local/hashql/core/src/lib.rs index 3e5b5ffbeba..daf94f95779 100644 --- a/libs/@local/hashql/core/src/lib.rs +++ b/libs/@local/hashql/core/src/lib.rs @@ -38,7 +38,8 @@ step_trait, sync_nonpoison, try_trait_v2, - variant_count + variant_count, + maybe_uninit_fill )] extern crate alloc; diff --git a/libs/@local/hashql/core/src/module/mod.rs b/libs/@local/hashql/core/src/module/mod.rs index ad56ed14a94..48e66fcc216 100644 --- a/libs/@local/hashql/core/src/module/mod.rs +++ b/libs/@local/hashql/core/src/module/mod.rs @@ -13,7 +13,10 @@ pub mod std_lib; pub mod universe; use core::{num::NonZero, slice}; -use std::sync::RwLock; +use std::{ + alloc::{Allocator, Global}, + sync::RwLock, +}; use self::{ error::{ResolutionError, ResolutionSuggestion}, @@ -73,7 +76,16 @@ impl<'heap> ModuleRegistry<'heap> { pub fn new(env: &Environment<'heap>) -> Self { let this = Self::empty(env.heap); - let mut std = StandardLibrary::new(env, &this); + let mut std = StandardLibrary::new(env, &this, Global); + std.register(); + + this + } + + pub fn new_in(env: &Environment<'heap>, alloc: S) -> Self { + let this = Self::empty(env.heap); + + let mut std = StandardLibrary::new(env, &this, alloc); std.register(); this diff --git a/libs/@local/hashql/core/src/module/std_lib/core/bits.rs b/libs/@local/hashql/core/src/module/std_lib/core/bits.rs index 4e8d7028f00..9df38c2e2ff 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/bits.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/bits.rs @@ -1,8 +1,10 @@ +use core::alloc::Allocator; + use super::func; use crate::{ module::{ locals::TypeDef, - std_lib::{ModuleDef, StandardLibrary, StandardLibraryModule, decl}, + std_lib::{ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, decl}, }, symbol::{Symbol, sym}, }; @@ -19,43 +21,46 @@ impl<'heap> StandardLibraryModule<'heap> for Bits { } #[expect(non_snake_case)] - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); - let Integer = lib.ty.integer(); + let Integer = context.ty.integer(); let items = [ ( sym::path::core::bits::and, &[sym::and, sym::symbol::ampersand], - decl!(lib; <>(lhs: Integer, rhs: Integer) -> Integer), + decl!(context; <>(lhs: Integer, rhs: Integer) -> Integer), ), ( sym::path::core::bits::or, &[sym::or, sym::symbol::pipe], - decl!(lib; <>(lhs: Integer, rhs: Integer) -> Integer), + decl!(context; <>(lhs: Integer, rhs: Integer) -> Integer), ), ( sym::path::core::bits::xor, &[sym::xor, sym::symbol::caret], - decl!(lib; <>(lhs: Integer, rhs: Integer) -> Integer), + decl!(context; <>(lhs: Integer, rhs: Integer) -> Integer), ), ( sym::path::core::bits::not, &[sym::not, sym::symbol::tilde], - decl!(lib; <>(value: Integer) -> Integer), + decl!(context; <>(value: Integer) -> Integer), ), ( sym::path::core::bits::shl, &[sym::shl, sym::symbol::ltlt], // In the future we might want to specialize the `shift` to `Natural` - decl!(lib; <>(value: Integer, shift: Integer) -> Integer), + decl!(context; <>(value: Integer, shift: Integer) -> Integer), ), ( sym::path::core::bits::shr, &[sym::shr, sym::symbol::gtgt], // In the future we might want to specialize the `shift` to `Natural` - decl!(lib; <>(value: Integer, shift: Integer) -> Integer), + decl!(context; <>(value: Integer, shift: Integer) -> Integer), ), ]; diff --git a/libs/@local/hashql/core/src/module/std_lib/core/bool.rs b/libs/@local/hashql/core/src/module/std_lib/core/bool.rs index 063e24345d7..0be4dbc177c 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/bool.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/bool.rs @@ -1,8 +1,10 @@ +use core::alloc::Allocator; + use super::func; use crate::{ module::{ locals::TypeDef, - std_lib::{ModuleDef, StandardLibrary, StandardLibraryModule, decl}, + std_lib::{ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, decl}, }, symbol::{Symbol, sym}, }; @@ -19,26 +21,29 @@ impl<'heap> StandardLibraryModule<'heap> for Bool { } #[expect(non_snake_case)] - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); - let Boolean = lib.ty.boolean(); + let Boolean = context.ty.boolean(); let items = [ ( sym::path::core::bool::not, &[sym::not, sym::symbol::exclamation], - decl!(lib; <>(value: Boolean) -> Boolean), + decl!(context; <>(value: Boolean) -> Boolean), ), ( sym::path::core::bool::and, &[sym::and, sym::symbol::ampamp], - decl!(lib; <>(lhs: Boolean, rhs: Boolean) -> Boolean), + decl!(context; <>(lhs: Boolean, rhs: Boolean) -> Boolean), ), ( sym::path::core::bool::or, &[sym::or, sym::symbol::pipepipe], - decl!(lib; <>(lhs: Boolean, rhs: Boolean) -> Boolean), + decl!(context; <>(lhs: Boolean, rhs: Boolean) -> Boolean), ), ]; diff --git a/libs/@local/hashql/core/src/module/std_lib/core/cmp.rs b/libs/@local/hashql/core/src/module/std_lib/core/cmp.rs index 23a2d5ab736..ed1bd68a242 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/cmp.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/cmp.rs @@ -1,8 +1,10 @@ +use core::alloc::Allocator; + use super::func; use crate::{ module::{ locals::TypeDef, - std_lib::{ModuleDef, StandardLibrary, StandardLibraryModule, decl}, + std_lib::{ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, decl}, }, symbol::{Symbol, sym}, }; @@ -19,42 +21,45 @@ impl<'heap> StandardLibraryModule<'heap> for Cmp { } #[expect(non_snake_case)] - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); - let Number = lib.ty.number(); - let Boolean = lib.ty.boolean(); + let Number = context.ty.number(); + let Boolean = context.ty.boolean(); let items = [ ( sym::path::core::cmp::gt, &[sym::gt, sym::symbol::gt], - decl!(lib; <>(lhs: Number, rhs: Number) -> Boolean), + decl!(context; <>(lhs: Number, rhs: Number) -> Boolean), ), ( sym::path::core::cmp::lt, &[sym::lt, sym::symbol::lt], - decl!(lib; <>(lhs: Number, rhs: Number) -> Boolean), + decl!(context; <>(lhs: Number, rhs: Number) -> Boolean), ), ( sym::path::core::cmp::gte, &[sym::gte, sym::symbol::gteq], - decl!(lib; <>(lhs: Number, rhs: Number) -> Boolean), + decl!(context; <>(lhs: Number, rhs: Number) -> Boolean), ), ( sym::path::core::cmp::lte, &[sym::lte, sym::symbol::lteq], - decl!(lib; <>(lhs: Number, rhs: Number) -> Boolean), + decl!(context; <>(lhs: Number, rhs: Number) -> Boolean), ), ( sym::path::core::cmp::eq, &[sym::eq, sym::symbol::eqeq], - decl!(lib; (lhs: T, rhs: U) -> Boolean), + decl!(context; (lhs: T, rhs: U) -> Boolean), ), ( sym::path::core::cmp::ne, &[sym::ne, sym::symbol::excleq], - decl!(lib; (lhs: T, rhs: U) -> Boolean), + decl!(context; (lhs: T, rhs: U) -> Boolean), ), ]; diff --git a/libs/@local/hashql/core/src/module/std_lib/core/json.rs b/libs/@local/hashql/core/src/module/std_lib/core/json.rs index 46dcbd0bd9f..852657487eb 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/json.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/json.rs @@ -1,5 +1,9 @@ +use core::alloc::Allocator; + use crate::{ - module::std_lib::{ItemDef, ModuleDef, StandardLibrary, StandardLibraryModule}, + module::std_lib::{ + ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + }, symbol::{Symbol, sym}, }; @@ -14,22 +18,27 @@ impl<'heap> StandardLibraryModule<'heap> for Json { sym::json } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); // type JsonPathSegment = String | Integer; // Note: The type should be Natural instead, but this requires refinement types - let json_path_segment_ty = lib.ty.union([lib.ty.string(), lib.ty.integer()]); + let json_path_segment_ty = context + .ty + .union([context.ty.string(), context.ty.integer()]); def.push( sym::JsonPathSegment, - ItemDef::r#type(lib.ty.env, json_path_segment_ty, &[]), + ItemDef::r#type(context.ty.env, json_path_segment_ty, &[]), ); // type JsonPath = JsonPathSegment[]; - let json_path_ty = lib.ty.list(json_path_segment_ty); + let json_path_ty = context.ty.list(json_path_segment_ty); def.push( sym::JsonPath, - ItemDef::r#type(lib.ty.env, json_path_ty, &[]), + ItemDef::r#type(context.ty.env, json_path_ty, &[]), ); def diff --git a/libs/@local/hashql/core/src/module/std_lib/core/math.rs b/libs/@local/hashql/core/src/module/std_lib/core/math.rs index 2690ca0715b..0ea55511105 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/math.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/math.rs @@ -1,8 +1,10 @@ +use core::alloc::Allocator; + use super::func; use crate::{ module::{ locals::TypeDef, - std_lib::{ModuleDef, StandardLibrary, StandardLibraryModule, decl}, + std_lib::{ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, decl}, }, symbol::{Symbol, sym}, }; @@ -19,42 +21,45 @@ impl<'heap> StandardLibraryModule<'heap> for Math { } #[expect(non_snake_case)] - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); - let Number = lib.ty.number(); - let Integer = lib.ty.integer(); + let Number = context.ty.number(); + let Integer = context.ty.integer(); let items = [ ( sym::path::core::math::add, &[sym::add, sym::symbol::plus] as &[Symbol<'heap>], - decl!(lib; (lhs: T, rhs: U) -> lib.ty.union([T, U])), + decl!(context; (lhs: T, rhs: U) -> context.ty.union([T, U])), ), ( sym::path::core::math::sub, &[sym::sub, sym::symbol::minus], - decl!(lib; (lhs: T, rhs: U) -> lib.ty.union([T, U])), + decl!(context; (lhs: T, rhs: U) -> context.ty.union([T, U])), ), ( sym::path::core::math::mul, &[sym::mul, sym::symbol::asterisk], - decl!(lib; (lhs: T, rhs: U) -> lib.ty.union([T, U])), + decl!(context; (lhs: T, rhs: U) -> context.ty.union([T, U])), ), ( sym::path::core::math::div, &[sym::div, sym::symbol::slash], - decl!(lib; <>(dividend: Number, divisor: Number) -> Number), + decl!(context; <>(dividend: Number, divisor: Number) -> Number), ), ( sym::path::core::math::rem, &[sym::rem, sym::symbol::percent], - decl!(lib; <>(dividend: Integer, divisor: Integer) -> Integer), + decl!(context; <>(dividend: Integer, divisor: Integer) -> Integer), ), ( sym::path::core::math::r#mod, &[sym::r#mod], - decl!(lib; <>(value: Integer, modulus: Integer) -> Integer), + decl!(context; <>(value: Integer, modulus: Integer) -> Integer), ), ( sym::path::core::math::pow, @@ -64,22 +69,22 @@ impl<'heap> StandardLibraryModule<'heap> for Math { sym::symbol::upwards, ], // (cannot be `Integer` on return, as `exponent` can be a negative integer) - decl!(lib; <>(base: Number, exponent: Number) -> Number), + decl!(context; <>(base: Number, exponent: Number) -> Number), ), ( sym::path::core::math::sqrt, &[sym::sqrt, sym::symbol::sqrt], - decl!(lib; <>(value: Number) -> Number), + decl!(context; <>(value: Number) -> Number), ), ( sym::path::core::math::cbrt, &[sym::cbrt, sym::symbol::cbrt], - decl!(lib; <>(value: Number) -> Number), + decl!(context; <>(value: Number) -> Number), ), ( sym::path::core::math::root, &[sym::root], // cannot use `ⁿ√` because `ⁿ` is a letter, not a symbol - decl!(lib; <>(value: Number, root: Number) -> Number), + decl!(context; <>(value: Number, root: Number) -> Number), ), ]; diff --git a/libs/@local/hashql/core/src/module/std_lib/core/mod.rs b/libs/@local/hashql/core/src/module/std_lib/core/mod.rs index 968dcda1593..e85015b822d 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/mod.rs @@ -1,4 +1,6 @@ -use super::{ItemDef, ModuleDef, StandardLibrary, StandardLibraryModule}; +use core::alloc::Allocator; + +use super::{ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule}; use crate::{ module::{item::IntrinsicValueItem, locals::TypeDef}, symbol::{Symbol, sym}, @@ -14,8 +16,8 @@ pub(in crate::module::std_lib) mod result; pub mod url; pub mod uuid; -pub(in crate::module::std_lib) fn func<'heap>( - def: &mut ModuleDef<'heap>, +pub(in crate::module::std_lib) fn func<'heap, S: Allocator>( + def: &mut ModuleDef<'heap, S>, path: Symbol<'heap>, names: impl IntoIterator>, @@ -46,7 +48,10 @@ impl<'heap> StandardLibraryModule<'heap> for Core { sym::core } - fn define(_: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - ModuleDef::new() + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + ModuleDef::new_in(context.alloc.clone()) } } diff --git a/libs/@local/hashql/core/src/module/std_lib/core/option.rs b/libs/@local/hashql/core/src/module/std_lib/core/option.rs index 1075b8ae7e3..41cc28e38aa 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/option.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/option.rs @@ -1,5 +1,9 @@ +use core::alloc::Allocator; + use crate::{ - module::std_lib::{ItemDef, ModuleDef, StandardLibrary, StandardLibraryModule}, + module::std_lib::{ + ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + }, symbol::{Symbol, sym}, }; @@ -30,30 +34,36 @@ impl<'heap> StandardLibraryModule<'heap> for Option { sym::option } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); // Option is simply a union between two opaque types, when the constructor only takes a // `Null` the constructor automatically allows for no-value. - let t_arg = lib.ty.fresh_argument(sym::T); - let t_ref = lib.ty.hydrate_argument(t_arg); - let t_param = lib.ty.param(t_arg); + let t_arg = context.ty.fresh_argument(sym::T); + let t_ref = context.ty.hydrate_argument(t_arg); + let t_param = context.ty.param(t_arg); // newtype None = Null; - let none_ty = lib.ty.opaque(sym::path::None, lib.ty.null()); - def.push(sym::None, ItemDef::newtype(lib.ty.env, none_ty, &[])); + let none_ty = context.ty.opaque(sym::path::None, context.ty.null()); + def.push(sym::None, ItemDef::newtype(context.ty.env, none_ty, &[])); // newtype Some = T; - let some_ty = lib + let some_ty = context .ty - .generic([(t_arg, None)], lib.ty.opaque(sym::path::Some, t_param)); - def.push(sym::Some, ItemDef::newtype(lib.ty.env, some_ty, &[t_ref])); + .generic([(t_arg, None)], context.ty.opaque(sym::path::Some, t_param)); + def.push( + sym::Some, + ItemDef::newtype(context.ty.env, some_ty, &[t_ref]), + ); // type Option = Some | None; - let option_ty = lib.ty.union([some_ty, none_ty]); + let option_ty = context.ty.union([some_ty, none_ty]); def.push( sym::Option, - ItemDef::r#type(lib.ty.env, option_ty, &[t_ref]), + ItemDef::r#type(context.ty.env, option_ty, &[t_ref]), ); def diff --git a/libs/@local/hashql/core/src/module/std_lib/core/result.rs b/libs/@local/hashql/core/src/module/std_lib/core/result.rs index 1b9237abf1e..f02f76549a1 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/result.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/result.rs @@ -1,5 +1,9 @@ +use core::alloc::Allocator; + use crate::{ - module::std_lib::{ItemDef, ModuleDef, StandardLibrary, StandardLibraryModule}, + module::std_lib::{ + ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + }, symbol::{Symbol, sym}, }; @@ -14,36 +18,39 @@ impl<'heap> StandardLibraryModule<'heap> for Result { sym::result } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); - let t_arg = lib.ty.fresh_argument(sym::T); - let t_ref = lib.ty.hydrate_argument(t_arg); - let t_param = lib.ty.param(t_arg); + let t_arg = context.ty.fresh_argument(sym::T); + let t_ref = context.ty.hydrate_argument(t_arg); + let t_param = context.ty.param(t_arg); - let e_arg = lib.ty.fresh_argument(sym::E); - let e_ref = lib.ty.hydrate_argument(e_arg); - let e_param = lib.ty.param(e_arg); + let e_arg = context.ty.fresh_argument(sym::E); + let e_ref = context.ty.hydrate_argument(e_arg); + let e_param = context.ty.param(e_arg); // newtype Ok = T; - let ok_ty = lib.ty.generic( + let ok_ty = context.ty.generic( [(t_arg, None)], - lib.ty.opaque(sym::path::core::result::Ok, t_param), + context.ty.opaque(sym::path::core::result::Ok, t_param), ); - def.push(sym::Ok, ItemDef::newtype(lib.ty.env, ok_ty, &[t_ref])); + def.push(sym::Ok, ItemDef::newtype(context.ty.env, ok_ty, &[t_ref])); // newtype Err = E; - let err_ty = lib.ty.generic( + let err_ty = context.ty.generic( [(e_arg, None)], - lib.ty.opaque(sym::path::core::result::Err, e_param), + context.ty.opaque(sym::path::core::result::Err, e_param), ); - def.push(sym::Err, ItemDef::newtype(lib.ty.env, err_ty, &[e_ref])); + def.push(sym::Err, ItemDef::newtype(context.ty.env, err_ty, &[e_ref])); // type Result = Ok | Err; - let result_ty = lib.ty.union([ok_ty, err_ty]); + let result_ty = context.ty.union([ok_ty, err_ty]); def.push( sym::Result, - ItemDef::newtype(lib.ty.env, result_ty, &[t_ref, e_ref]), + ItemDef::newtype(context.ty.env, result_ty, &[t_ref, e_ref]), ); def diff --git a/libs/@local/hashql/core/src/module/std_lib/core/url.rs b/libs/@local/hashql/core/src/module/std_lib/core/url.rs index 2cfb8ff0f8e..2b7d2bbf7eb 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/url.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/url.rs @@ -1,5 +1,9 @@ +use core::alloc::Allocator; + use crate::{ - module::std_lib::{ItemDef, ModuleDef, StandardLibrary, StandardLibraryModule}, + module::std_lib::{ + ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + }, symbol::{Symbol, sym}, }; @@ -25,13 +29,16 @@ impl<'heap> StandardLibraryModule<'heap> for Url { sym::url } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); // TODO: consider making this constructor private via intrinsic (requires VM) // newtype Url = String; - let url_ty = types::url(&lib.ty); - def.push(sym::Url, ItemDef::newtype(lib.ty.env, url_ty, &[])); + let url_ty = types::url(&context.ty); + def.push(sym::Url, ItemDef::newtype(context.ty.env, url_ty, &[])); def } diff --git a/libs/@local/hashql/core/src/module/std_lib/core/uuid.rs b/libs/@local/hashql/core/src/module/std_lib/core/uuid.rs index de7ae31ca65..d3e80be57ac 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/uuid.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/uuid.rs @@ -1,5 +1,9 @@ +use core::alloc::Allocator; + use crate::{ - module::std_lib::{ItemDef, ModuleDef, StandardLibrary, StandardLibraryModule}, + module::std_lib::{ + ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + }, symbol::{Symbol, sym}, }; @@ -25,12 +29,15 @@ impl<'heap> StandardLibraryModule<'heap> for Uuid { sym::uuid } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); // TODO: consider making this constructor private via intrinsic (requires VM) // newtype Uuid = String; - let uuid = ItemDef::newtype(lib.ty.env, types::uuid(&lib.ty), &[]); + let uuid = ItemDef::newtype(context.ty.env, types::uuid(&context.ty), &[]); def.push(sym::Uuid, uuid); def diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/body.rs b/libs/@local/hashql/core/src/module/std_lib/graph/body.rs index a49263997bc..c2176f8f7ec 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/body.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/body.rs @@ -1,8 +1,12 @@ +use core::alloc::Allocator; + use crate::{ module::{ - StandardLibrary, locals::TypeDef, - std_lib::{self, ModuleDef, StandardLibraryModule, core::func, decl}, + std_lib::{ + self, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + core::func, decl, + }, }, symbol::{Symbol, sym}, }; @@ -18,24 +22,27 @@ impl<'heap> StandardLibraryModule<'heap> for Body { sym::body } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); - let graph = lib.manifest::(); + let graph = cache.request::(context); let mut graph_param = graph.expect_type(sym::Graph); let mut graph_returns = graph_param; - graph_param.instantiate(&mut lib.instantiate); - graph_returns.instantiate(&mut lib.instantiate); + graph_param.instantiate(&mut context.instantiate); + graph_returns.instantiate(&mut context.instantiate); // `filter(graph: Graph, predicate: fn(vertex: T) -> bool) -> Graph;` // Once https://linear.app/hash/issue/H-4741/hashql-support-for-type-guards lands this will change to: // `filter(graph: Graph, predicate: fn(vertex: T) -> entity is U) -> Graph;` - let decl = decl!(lib; - (graph: lib.ty.apply([(graph_param.arguments[0].id, T)], graph_param.id), - predicate: lib.ty.closure([T], lib.ty.boolean()) - ) -> lib.ty.apply([(graph_returns.arguments[0].id, T)], graph_returns.id) + let decl = decl!(context; + (graph: context.ty.apply([(graph_param.arguments[0].id, T)], graph_param.id), + predicate: context.ty.closure([T], context.ty.boolean()) + ) -> context.ty.apply([(graph_returns.arguments[0].id, T)], graph_returns.id) ); func(&mut def, sym::path::graph_body_filter, [sym::filter], decl); diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/entity.rs b/libs/@local/hashql/core/src/module/std_lib/graph/entity.rs index 461e4125633..90b1515a551 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/entity.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/entity.rs @@ -1,9 +1,10 @@ +use core::alloc::Allocator; + use crate::{ module::{ - StandardLibrary, locals::TypeDef, std_lib::{ - self, ModuleDef, StandardLibraryModule, + self, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, core::{func, option::types::option}, decl, }, @@ -25,30 +26,33 @@ impl<'heap> StandardLibraryModule<'heap> for Entity { sym::entity } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); - let mut entity_ty = lib - .manifest::() + let mut entity_ty = cache + .request::(context) .expect_newtype(sym::Entity); - entity_ty.instantiate(&mut lib.instantiate); + entity_ty.instantiate(&mut context.instantiate); - let versioned_url_ty = lib - .manifest::() + let versioned_url_ty = cache + .request::(context) .expect_newtype(sym::VersionedUrl); - let json_path_ty = lib - .manifest::() + let json_path_ty = cache + .request::(context) .expect_type(sym::JsonPath); // `is_of_type(entity: Entity, depth: Integer, type: VersionedUrl) -> Boolean` // see: https://linear.app/hash/issue/H-4741/hashql-support-for-type-guards // see: https://linear.app/hash/issue/H-4742/hashql-allow-is-of-type-to-be-queried-using-an-entitytype - let decl = decl!(lib; - (entity: lib.ty.apply([(entity_ty.arguments[0].id, T)], entity_ty.id), - depth: lib.ty.integer(), + let decl = decl!(context; + (entity: context.ty.apply([(entity_ty.arguments[0].id, T)], entity_ty.id), + depth: context.ty.integer(), type: versioned_url_ty.id - ) -> lib.ty.boolean() + ) -> context.ty.boolean() ); func( @@ -59,10 +63,10 @@ impl<'heap> StandardLibraryModule<'heap> for Entity { ); // `property(entity: Entity, path: JsonPath) -> Option` - let decl = decl!(lib; - (entity: lib.ty.apply([(entity_ty.arguments[0].id, T)], entity_ty.id), + let decl = decl!(context; + (entity: context.ty.apply([(entity_ty.arguments[0].id, T)], entity_ty.id), path: json_path_ty.id - ) -> option(&lib.ty, lib.ty.unknown()) + ) -> option(&context.ty, context.ty.unknown()) ); func( diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/head.rs b/libs/@local/hashql/core/src/module/std_lib/graph/head.rs index dd983bfe8b3..60fcf253ba2 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/head.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/head.rs @@ -1,8 +1,12 @@ +use core::alloc::Allocator; + use crate::{ module::{ - StandardLibrary, locals::TypeDef, - std_lib::{self, ModuleDef, StandardLibraryModule, core::func, decl}, + std_lib::{ + self, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + core::func, decl, + }, }, symbol::{Symbol, sym}, }; @@ -22,29 +26,33 @@ impl<'heap> StandardLibraryModule<'heap> for Head { sym::head } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); - let query_temporal_axes_ty = lib - .manifest::() + let query_temporal_axes_ty = cache + .request::(context) .expect_type(sym::QueryTemporalAxes); - let mut graph_ty = lib - .manifest::() + let mut graph_ty = cache + .request::(context) .expect_type(sym::Graph); - graph_ty.instantiate(&mut lib.instantiate); + graph_ty.instantiate(&mut context.instantiate); - let mut entity = lib - .manifest::() + let mut entity = cache + .request::(context) .expect_newtype(sym::Entity); - entity.instantiate(&mut lib.instantiate); + entity.instantiate(&mut context.instantiate); // ::graph::head::entities(axis: TimeAxis) -> Graph>; - let entities_returns = lib.ty.apply( + let entities_returns = context.ty.apply( [( graph_ty.arguments[0].id, - lib.ty - .apply([(entity.arguments[0].id, lib.ty.unknown())], entity.id), + context + .ty + .apply([(entity.arguments[0].id, context.ty.unknown())], entity.id), )], graph_ty.id, ); @@ -52,7 +60,7 @@ impl<'heap> StandardLibraryModule<'heap> for Head { &mut def, sym::path::graph_head_entities, [sym::entities], - decl!(lib; <>(axis: query_temporal_axes_ty.id) -> entities_returns), + decl!(context; <>(axis: query_temporal_axes_ty.id) -> entities_returns), ); def diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/mod.rs b/libs/@local/hashql/core/src/module/std_lib/graph/mod.rs index fd09dd9b5c0..8a5d74dac82 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/mod.rs @@ -1,3 +1,12 @@ +use core::alloc::Allocator; + +use crate::{ + module::std_lib::{ + ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + }, + symbol::{Symbol, sym}, +}; + pub(in crate::module::std_lib) mod body; pub(in crate::module::std_lib) mod entity; pub(in crate::module::std_lib) mod head; @@ -6,14 +15,6 @@ pub mod temporal; pub(in crate::module::std_lib) mod tmp; pub mod types; -use crate::{ - module::{ - StandardLibrary, - std_lib::{ItemDef, ModuleDef, StandardLibraryModule}, - }, - symbol::{Symbol, sym}, -}; - pub(in crate::module::std_lib) struct Graph { _dependencies: (), } @@ -33,8 +34,11 @@ impl<'heap> StandardLibraryModule<'heap> for Graph { sym::graph } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); // newtype Graph = ('marker: T) // @@ -42,22 +46,22 @@ impl<'heap> StandardLibraryModule<'heap> for Graph { // internally to track the graph's type information. The field is named `'marker` using an // identifier that cannot be referenced in user code (`'` is not a valid symbol in any // identifier). - let t_arg = lib.ty.fresh_argument(sym::T); - let t_ref = lib.ty.hydrate_argument(t_arg); - let t_param = lib.ty.param(t_arg); + let t_arg = context.ty.fresh_argument(sym::T); + let t_ref = context.ty.hydrate_argument(t_arg); + let t_param = context.ty.param(t_arg); - let graph_ty = lib.ty.generic( + let graph_ty = context.ty.generic( [t_arg], - lib.ty.opaque( + context.ty.opaque( sym::path::graph::Graph, - lib.ty.r#struct([(sym::internal::marker, t_param)]), + context.ty.r#struct([(sym::internal::marker, t_param)]), ), ); def.push( sym::Graph, // Export as `type` rather than `newtype` since Graph is not intended to be // user-constructible - ItemDef::r#type(lib.ty.env, graph_ty, &[t_ref]), + ItemDef::r#type(context.ty.env, graph_ty, &[t_ref]), ); def diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/tail.rs b/libs/@local/hashql/core/src/module/std_lib/graph/tail.rs index 5cf3e2ec91e..a38b51317c6 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/tail.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/tail.rs @@ -1,8 +1,12 @@ +use core::alloc::Allocator; + use crate::{ module::{ - StandardLibrary, locals::TypeDef, - std_lib::{self, ModuleDef, StandardLibraryModule, core::func, decl}, + std_lib::{ + self, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + core::func, decl, + }, }, symbol::{Symbol, sym}, }; @@ -18,17 +22,20 @@ impl<'heap> StandardLibraryModule<'heap> for Tail { sym::tail } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); - let graph = lib.manifest::(); + let graph = cache.request::(context); let mut graph_ty = graph.expect_type(sym::Graph); - graph_ty.instantiate(&mut lib.instantiate); + graph_ty.instantiate(&mut context.instantiate); // `collect(graph: Graph) -> List;` - let decl = decl!(lib; - (graph: lib.ty.apply([(graph_ty.arguments[0].id, T)], graph_ty.id)) -> lib.ty.list(T) + let decl = decl!(context; + (graph: context.ty.apply([(graph_ty.arguments[0].id, T)], graph_ty.id)) -> context.ty.list(T) ); func( diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/temporal.rs b/libs/@local/hashql/core/src/module/std_lib/graph/temporal.rs index c5c1eb07f94..490518fe66d 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/temporal.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/temporal.rs @@ -1,5 +1,9 @@ +use core::alloc::Allocator; + use crate::{ - module::std_lib::{ItemDef, ModuleDef, StandardLibrary, StandardLibraryModule}, + module::std_lib::{ + ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + }, symbol::{Symbol, sym}, }; @@ -113,170 +117,182 @@ impl<'heap> StandardLibraryModule<'heap> for Temporal { } #[expect(clippy::too_many_lines, clippy::similar_names)] - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); // newtype Timestamp = Integer // // TODO: replace with a dedicated primitive type in the future. - let timestamp_ty = types::timestamp(&lib.ty); + let timestamp_ty = types::timestamp(&context.ty); def.push( sym::Timestamp, - ItemDef::newtype(lib.ty.env, timestamp_ty, &[]), + ItemDef::newtype(context.ty.env, timestamp_ty, &[]), ); // newtype DecisionTime = T - let dt_t_arg = lib.ty.fresh_argument(sym::T); - let dt_t_ref = lib.ty.hydrate_argument(dt_t_arg); - let dt_t_param = lib.ty.param(dt_t_arg); + let dt_t_arg = context.ty.fresh_argument(sym::T); + let dt_t_ref = context.ty.hydrate_argument(dt_t_arg); + let dt_t_param = context.ty.param(dt_t_arg); - let decision_time_ty = lib.ty.generic( + let decision_time_ty = context.ty.generic( [(dt_t_arg, None)], - types::decision_time(&lib.ty, dt_t_param), + types::decision_time(&context.ty, dt_t_param), ); def.push( sym::DecisionTime, - ItemDef::newtype(lib.ty.env, decision_time_ty, &[dt_t_ref]), + ItemDef::newtype(context.ty.env, decision_time_ty, &[dt_t_ref]), ); // newtype TransactionTime = T - let tt_t_arg = lib.ty.fresh_argument(sym::T); - let tt_t_ref = lib.ty.hydrate_argument(tt_t_arg); - let tt_t_param = lib.ty.param(tt_t_arg); + let tt_t_arg = context.ty.fresh_argument(sym::T); + let tt_t_ref = context.ty.hydrate_argument(tt_t_arg); + let tt_t_param = context.ty.param(tt_t_arg); - let transaction_time_ty = lib.ty.generic( + let transaction_time_ty = context.ty.generic( [(tt_t_arg, None)], - types::transaction_time(&lib.ty, tt_t_param), + types::transaction_time(&context.ty, tt_t_param), ); def.push( sym::TransactionTime, - ItemDef::newtype(lib.ty.env, transaction_time_ty, &[tt_t_ref]), + ItemDef::newtype(context.ty.env, transaction_time_ty, &[tt_t_ref]), ); // newtype UnboundedTemporalBound = Null - let unbounded_bound_ty = types::unbounded_temporal_bound(&lib.ty); + let unbounded_bound_ty = types::unbounded_temporal_bound(&context.ty); def.push( sym::UnboundedTemporalBound, - ItemDef::newtype(lib.ty.env, unbounded_bound_ty, &[]), + ItemDef::newtype(context.ty.env, unbounded_bound_ty, &[]), ); // newtype InclusiveTemporalBound = Timestamp - let inclusive_bound_ty = types::inclusive_temporal_bound(&lib.ty); + let inclusive_bound_ty = types::inclusive_temporal_bound(&context.ty); def.push( sym::InclusiveTemporalBound, - ItemDef::newtype(lib.ty.env, inclusive_bound_ty, &[]), + ItemDef::newtype(context.ty.env, inclusive_bound_ty, &[]), ); // newtype ExclusiveTemporalBound = Timestamp - let exclusive_bound_ty = types::exclusive_temporal_bound(&lib.ty); + let exclusive_bound_ty = types::exclusive_temporal_bound(&context.ty); def.push( sym::ExclusiveTemporalBound, - ItemDef::newtype(lib.ty.env, exclusive_bound_ty, &[]), + ItemDef::newtype(context.ty.env, exclusive_bound_ty, &[]), ); // type TemporalBound = UnboundedTemporalBound | InclusiveTemporalBound // | ExclusiveTemporalBound let temporal_bound_ty = - lib.ty + context + .ty .union([unbounded_bound_ty, inclusive_bound_ty, exclusive_bound_ty]); def.push( sym::TemporalBound, - ItemDef::r#type(lib.ty.env, temporal_bound_ty, &[]), + ItemDef::r#type(context.ty.env, temporal_bound_ty, &[]), ); // type FiniteTemporalBound = InclusiveTemporalBound | ExclusiveTemporalBound - let finite_bound_ty = lib.ty.union([inclusive_bound_ty, exclusive_bound_ty]); + let finite_bound_ty = context.ty.union([inclusive_bound_ty, exclusive_bound_ty]); def.push( sym::FiniteTemporalBound, - ItemDef::r#type(lib.ty.env, finite_bound_ty, &[]), + ItemDef::r#type(context.ty.env, finite_bound_ty, &[]), ); // type OpenTemporalBound = ExclusiveTemporalBound | UnboundedTemporalBound - let open_bound_ty = lib.ty.union([exclusive_bound_ty, unbounded_bound_ty]); + let open_bound_ty = context.ty.union([exclusive_bound_ty, unbounded_bound_ty]); def.push( sym::OpenTemporalBound, - ItemDef::r#type(lib.ty.env, open_bound_ty, &[]), + ItemDef::r#type(context.ty.env, open_bound_ty, &[]), ); // newtype Interval = (start: S, end: E) - let interval_s_arg = lib.ty.fresh_argument(sym::S); - let interval_s_ref = lib.ty.hydrate_argument(interval_s_arg); - let interval_s_param = lib.ty.param(interval_s_arg); + let interval_s_arg = context.ty.fresh_argument(sym::S); + let interval_s_ref = context.ty.hydrate_argument(interval_s_arg); + let interval_s_param = context.ty.param(interval_s_arg); - let interval_e_arg = lib.ty.fresh_argument(sym::E); - let interval_e_ref = lib.ty.hydrate_argument(interval_e_arg); - let interval_e_param = lib.ty.param(interval_e_arg); + let interval_e_arg = context.ty.fresh_argument(sym::E); + let interval_e_ref = context.ty.hydrate_argument(interval_e_arg); + let interval_e_param = context.ty.param(interval_e_arg); - let interval_ty = lib.ty.generic( + let interval_ty = context.ty.generic( [(interval_s_arg, None), (interval_e_arg, None)], - types::interval(&lib.ty, interval_s_param, interval_e_param), + types::interval(&context.ty, interval_s_param, interval_e_param), ); def.push( sym::Interval, - ItemDef::newtype(lib.ty.env, interval_ty, &[interval_s_ref, interval_e_ref]), + ItemDef::newtype( + context.ty.env, + interval_ty, + &[interval_s_ref, interval_e_ref], + ), ); // type LeftClosedTemporalInterval = // Interval - let left_closed_interval_ty = types::interval(&lib.ty, inclusive_bound_ty, open_bound_ty); + let left_closed_interval_ty = + types::interval(&context.ty, inclusive_bound_ty, open_bound_ty); def.push( sym::LeftClosedTemporalInterval, - ItemDef::r#type(lib.ty.env, left_closed_interval_ty, &[]), + ItemDef::r#type(context.ty.env, left_closed_interval_ty, &[]), ); // type RightBoundedTemporalInterval = // Interval let right_bounded_interval_ty = - types::interval(&lib.ty, temporal_bound_ty, finite_bound_ty); + types::interval(&context.ty, temporal_bound_ty, finite_bound_ty); def.push( sym::RightBoundedTemporalInterval, - ItemDef::r#type(lib.ty.env, right_bounded_interval_ty, &[]), + ItemDef::r#type(context.ty.env, right_bounded_interval_ty, &[]), ); // newtype PinnedTransactionTimeTemporalAxes = ( // pinned: TransactionTime, // variable: DecisionTime, // ) - let pinned_tx_ty = lib.ty.opaque( + let pinned_tx_ty = context.ty.opaque( sym::path::PinnedTransactionTimeTemporalAxes, - lib.ty.r#struct([ - (sym::pinned, types::transaction_time(&lib.ty, timestamp_ty)), + context.ty.r#struct([ + ( + sym::pinned, + types::transaction_time(&context.ty, timestamp_ty), + ), ( sym::variable, - types::decision_time(&lib.ty, right_bounded_interval_ty), + types::decision_time(&context.ty, right_bounded_interval_ty), ), ]), ); def.push( sym::PinnedTransactionTimeTemporalAxes, - ItemDef::newtype(lib.ty.env, pinned_tx_ty, &[]), + ItemDef::newtype(context.ty.env, pinned_tx_ty, &[]), ); // newtype PinnedDecisionTimeTemporalAxes = ( // pinned: DecisionTime, // variable: TransactionTime, // ) - let pinned_dt_ty = lib.ty.opaque( + let pinned_dt_ty = context.ty.opaque( sym::path::PinnedDecisionTimeTemporalAxes, - lib.ty.r#struct([ - (sym::pinned, types::decision_time(&lib.ty, timestamp_ty)), + context.ty.r#struct([ + (sym::pinned, types::decision_time(&context.ty, timestamp_ty)), ( sym::variable, - types::transaction_time(&lib.ty, right_bounded_interval_ty), + types::transaction_time(&context.ty, right_bounded_interval_ty), ), ]), ); def.push( sym::PinnedDecisionTimeTemporalAxes, - ItemDef::newtype(lib.ty.env, pinned_dt_ty, &[]), + ItemDef::newtype(context.ty.env, pinned_dt_ty, &[]), ); // type QueryTemporalAxes = PinnedTransactionTimeTemporalAxes // | PinnedDecisionTimeTemporalAxes - let query_temporal_axes_ty = lib.ty.union([pinned_tx_ty, pinned_dt_ty]); + let query_temporal_axes_ty = context.ty.union([pinned_tx_ty, pinned_dt_ty]); def.push( sym::QueryTemporalAxes, - ItemDef::r#type(lib.ty.env, query_temporal_axes_ty, &[]), + ItemDef::r#type(context.ty.env, query_temporal_axes_ty, &[]), ); def diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/tmp.rs b/libs/@local/hashql/core/src/module/std_lib/graph/tmp.rs index e85867fb5b0..02d5aa61197 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/tmp.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/tmp.rs @@ -1,8 +1,11 @@ +use core::alloc::Allocator; + use crate::{ module::{ - StandardLibrary, locals::TypeDef, - std_lib::{self, ModuleDef, StandardLibraryModule, core::func}, + std_lib::{ + self, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, core::func, + }, }, symbol::{Symbol, sym}, r#type::TypeId, @@ -19,11 +22,14 @@ impl<'heap> StandardLibraryModule<'heap> for Tmp { sym::tmp } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); - let query_temporal_axes_ty = lib - .manifest::() + let query_temporal_axes_ty = cache + .request::(context) .expect_type(sym::QueryTemporalAxes); // ::graph::tmp::decision_time_now() -> TimeAxis @@ -32,8 +38,10 @@ impl<'heap> StandardLibraryModule<'heap> for Tmp { sym::path::graph::tmp::decision_time_now, [sym::decision_time_now], TypeDef { - id: lib.ty.closure([] as [TypeId; 0], query_temporal_axes_ty.id), - arguments: lib.ty.env.intern_generic_argument_references(&[]), + id: context + .ty + .closure([] as [TypeId; 0], query_temporal_axes_ty.id), + arguments: context.ty.env.intern_generic_argument_references(&[]), }, ); diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/entity.rs b/libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/entity.rs index c89d11c7ee5..c2952938587 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/entity.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/entity.rs @@ -1,7 +1,8 @@ +use core::alloc::Allocator; + use crate::{ - module::{ - StandardLibrary, - std_lib::{self, ItemDef, ModuleDef, StandardLibraryModule}, + module::std_lib::{ + self, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, }, symbol::{Symbol, sym}, }; @@ -377,27 +378,30 @@ impl<'heap> StandardLibraryModule<'heap> for Entity { } #[expect(clippy::too_many_lines)] - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); - - let uuid_ty = lib - .manifest::() + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); + + let uuid_ty = cache + .request::(context) .expect_newtype(sym::Uuid) .id; - let web_id_ty = lib - .manifest::() + let web_id_ty = cache + .request::(context) .expect_newtype(sym::WebId) .id; - let versioned_url_ty = lib - .manifest::() + let versioned_url_ty = cache + .request::(context) .expect_newtype(sym::VersionedUrl) .id; - let left_closed_interval_ty = lib - .manifest::() + let left_closed_interval_ty = cache + .request::(context) .expect_type(sym::LeftClosedTemporalInterval) .id; - let ty = &lib.ty; + let ty = &context.ty; let entity_uuid_ty = types::entity_uuid(ty, Some(types::EntityUuidDependencies { uuid: uuid_ty })); @@ -523,13 +527,13 @@ impl<'heap> StandardLibraryModule<'heap> for Entity { ); // Entity - let t_arg = lib.ty.fresh_argument(sym::T); - let t_ref = lib.ty.hydrate_argument(t_arg); - let t_param = lib.ty.param(t_arg); - let entity_ty = lib.ty.generic( + let t_arg = context.ty.fresh_argument(sym::T); + let t_ref = context.ty.hydrate_argument(t_arg); + let t_param = context.ty.param(t_arg); + let entity_ty = context.ty.generic( [t_arg], types::entity( - &lib.ty, + &context.ty, t_param, Some(types::EntityDependencies { link_data: link_data_ty, @@ -540,7 +544,7 @@ impl<'heap> StandardLibraryModule<'heap> for Entity { ); def.push( sym::Entity, - ItemDef::newtype(lib.ty.env, entity_ty, &[t_ref]), + ItemDef::newtype(context.ty.env, entity_ty, &[t_ref]), ); def diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/mod.rs b/libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/mod.rs index 39359faff79..115bdd89c9a 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/mod.rs @@ -1,10 +1,9 @@ +use core::alloc::Allocator; + pub mod entity; use crate::{ - module::{ - StandardLibrary, - std_lib::{ModuleDef, StandardLibraryModule}, - }, + module::std_lib::{ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule}, symbol::{Symbol, sym}, }; @@ -17,7 +16,10 @@ impl<'heap> StandardLibraryModule<'heap> for Knowledge { sym::knowledge } - fn define(_: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - ModuleDef::new() + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + ModuleDef::new_in(context.alloc.clone()) } } diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/types/mod.rs b/libs/@local/hashql/core/src/module/std_lib/graph/types/mod.rs index 3c6ae5eeabd..0e01e828215 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/types/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/types/mod.rs @@ -1,9 +1,8 @@ +use core::alloc::Allocator; + // This is in a separate module, to facilitate: https://linear.app/hash/issue/H-4735/hashql-convert-rust-types-into-hashql-types use crate::{ - module::{ - StandardLibrary, - std_lib::{ModuleDef, StandardLibraryModule}, - }, + module::std_lib::{ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule}, symbol::{Symbol, sym}, }; @@ -26,7 +25,10 @@ impl<'heap> StandardLibraryModule<'heap> for Types { sym::types } - fn define(_: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - ModuleDef::new() + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + ModuleDef::new_in(context.alloc.clone()) } } diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/entity_type.rs b/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/entity_type.rs index 03346c804c3..fe4afe57e85 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/entity_type.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/entity_type.rs @@ -1,7 +1,9 @@ +use core::alloc::Allocator; + use crate::{ - module::{ - StandardLibrary, - std_lib::{self, ItemDef, ModuleDef, StandardLibraryModule, core::option::types::option}, + module::std_lib::{ + self, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + core::option::types::option, }, symbol::{Symbol, sym}, }; @@ -20,36 +22,41 @@ impl<'heap> StandardLibraryModule<'heap> for EntityType { sym::entity_type } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); // newtype EntityTypeMetadata = (web_id: Option) - let web_id = lib - .manifest::() + let web_id = cache + .request::(context) .expect_newtype(sym::WebId); - let entity_type_metadata_ty = lib.ty.opaque( + let entity_type_metadata_ty = context.ty.opaque( sym::path::graph::types::ontology::entity_type::EntityTypeMetadata, - lib.ty.r#struct([(sym::web_id, option(&lib.ty, web_id.id))]), + context + .ty + .r#struct([(sym::web_id, option(&context.ty, web_id.id))]), ); def.push( sym::EntityTypeMetadata, - ItemDef::newtype(lib.ty.env, entity_type_metadata_ty, &[]), + ItemDef::newtype(context.ty.env, entity_type_metadata_ty, &[]), ); // newtype EntityType = (id: VersionedUrl, metadata: EntityTypeMetadata) - let versioned_url = lib - .manifest::() + let versioned_url = cache + .request::(context) .expect_newtype(sym::VersionedUrl); - let entity_id_ty = lib.ty.opaque( + let entity_id_ty = context.ty.opaque( sym::path::graph::types::ontology::entity_type::EntityType, - lib.ty.r#struct([ + context.ty.r#struct([ (sym::id, versioned_url.id), (sym::metadata, entity_type_metadata_ty), ]), ); def.push( sym::EntityType, - ItemDef::newtype(lib.ty.env, entity_id_ty, &[]), + ItemDef::newtype(context.ty.env, entity_id_ty, &[]), ); def diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/mod.rs b/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/mod.rs index ed9b49b76a6..69e9f3ecabe 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/mod.rs @@ -1,7 +1,8 @@ +use core::alloc::Allocator; + use crate::{ - module::{ - StandardLibrary, - std_lib::{self, ItemDef, ModuleDef, StandardLibraryModule}, + module::std_lib::{ + self, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, }, symbol::{Symbol, sym}, }; @@ -69,30 +70,38 @@ impl<'heap> StandardLibraryModule<'heap> for Ontology { sym::ontology } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); // newtype OntologyTypeVersion = String; - let ontology_type_version_ty = types::ontology_type_version(&lib.ty); + let ontology_type_version_ty = types::ontology_type_version(&context.ty); def.push( sym::OntologyTypeVersion, - ItemDef::newtype(lib.ty.env, ontology_type_version_ty, &[]), + ItemDef::newtype(context.ty.env, ontology_type_version_ty, &[]), ); - let url_ty = lib - .manifest::() + let url_ty = cache + .request::(context) .expect_newtype(sym::Url) .id; // TODO: consider making this constructor private via intrinsic (requires VM) // newtype BaseUrl = Url; - let base_url_ty = - types::base_url(&lib.ty, Some(types::BaseUrlDependencies { url: url_ty })); - def.push(sym::BaseUrl, ItemDef::newtype(lib.ty.env, base_url_ty, &[])); + let base_url_ty = types::base_url( + &context.ty, + Some(types::BaseUrlDependencies { url: url_ty }), + ); + def.push( + sym::BaseUrl, + ItemDef::newtype(context.ty.env, base_url_ty, &[]), + ); // newtype VersionedUrl = (base_url: BaseUrl, version: OntologyTypeVersion); let versioned_url_ty = types::versioned_url( - &lib.ty, + &context.ty, Some(types::VersionedUrlDependencies { base_url: base_url_ty, ontology_type_version: ontology_type_version_ty, @@ -100,7 +109,7 @@ impl<'heap> StandardLibraryModule<'heap> for Ontology { ); def.push( sym::VersionedUrl, - ItemDef::newtype(lib.ty.env, versioned_url_ty, &[]), + ItemDef::newtype(context.ty.env, versioned_url_ty, &[]), ); def diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/mod.rs b/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/mod.rs index fdbbe962a42..480b99545e7 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/mod.rs @@ -1,7 +1,8 @@ +use core::alloc::Allocator; + use crate::{ - module::{ - StandardLibrary, - std_lib::{self, ItemDef, ModuleDef, StandardLibraryModule}, + module::std_lib::{ + self, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, }, symbol::{Symbol, sym}, }; @@ -44,24 +45,27 @@ impl<'heap> StandardLibraryModule<'heap> for ActorGroup { sym::actor_group } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); // newtype ActorGroupEntityUuid = EntityUuid; // (we just set it to Uuid to avoid any cycles) // see: https://linear.app/hash/issue/H-4616/hashql-use-expression-hoisting // see: https://linear.app/hash/issue/H-4735/hashql-convert-rust-types-into-hashql-types - let uuid_ty = lib - .manifest::() + let uuid_ty = cache + .request::(context) .expect_newtype(sym::Uuid) .id; let actor_group_entity_uuid_ty = types::actor_group_entity_uuid( - &lib.ty, + &context.ty, Some(types::ActorGroupEntityUuidDependencies { uuid: uuid_ty }), ); def.push( sym::ActorGroupEntityUuid, - ItemDef::newtype(lib.ty.env, actor_group_entity_uuid_ty, &[]), + ItemDef::newtype(context.ty.env, actor_group_entity_uuid_ty, &[]), ); def diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/web.rs b/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/web.rs index 18d196e246e..02d04353689 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/web.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/web.rs @@ -1,7 +1,8 @@ +use core::alloc::Allocator; + use crate::{ - module::{ - StandardLibrary, - std_lib::{self, ItemDef, ModuleDef, StandardLibraryModule}, + module::std_lib::{ + self, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, }, symbol::{Symbol, sym}, }; @@ -43,21 +44,24 @@ impl<'heap> StandardLibraryModule<'heap> for Web { sym::web } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); // newtype WebId = ActorGroupEntityUuid; - let actor_group_entity_uuid_ty = lib - .manifest::() + let actor_group_entity_uuid_ty = cache + .request::(context) .expect_newtype(sym::ActorGroupEntityUuid) .id; let web_id_ty = types::web_id( - &lib.ty, + &context.ty, Some(types::WebIdDependencies { actor_group_entity_uuid: actor_group_entity_uuid_ty, }), ); - def.push(sym::WebId, ItemDef::newtype(lib.ty.env, web_id_ty, &[])); + def.push(sym::WebId, ItemDef::newtype(context.ty.env, web_id_ty, &[])); def } diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/mod.rs b/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/mod.rs index 82dbda1c525..75bc1936810 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/mod.rs @@ -1,10 +1,9 @@ +use core::alloc::Allocator; + pub mod actor_group; use crate::{ - module::{ - StandardLibrary, - std_lib::{ModuleDef, StandardLibraryModule}, - }, + module::std_lib::{ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule}, symbol::{Symbol, sym}, }; @@ -19,7 +18,10 @@ impl<'heap> StandardLibraryModule<'heap> for Principal { sym::principal } - fn define(_: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - ModuleDef::new() + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + ModuleDef::new_in(context.alloc.clone()) } } diff --git a/libs/@local/hashql/core/src/module/std_lib/kernel/mod.rs b/libs/@local/hashql/core/src/module/std_lib/kernel/mod.rs index e7a23ecf350..7ca71c5ee75 100644 --- a/libs/@local/hashql/core/src/module/std_lib/kernel/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/kernel/mod.rs @@ -1,7 +1,9 @@ +use core::alloc::Allocator; + pub(in crate::module::std_lib) mod special_form; pub(in crate::module::std_lib) mod r#type; -use super::{ModuleDef, StandardLibrary, StandardLibraryModule}; +use super::{ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule}; use crate::symbol::{Symbol, sym}; pub(in crate::module::std_lib) struct Kernel; @@ -13,7 +15,10 @@ impl<'heap> StandardLibraryModule<'heap> for Kernel { sym::kernel } - fn define(_: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - ModuleDef::new() + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + ModuleDef::new_in(context.alloc.clone()) } } diff --git a/libs/@local/hashql/core/src/module/std_lib/kernel/special_form.rs b/libs/@local/hashql/core/src/module/std_lib/kernel/special_form.rs index b7f981f273b..6b175d67f42 100644 --- a/libs/@local/hashql/core/src/module/std_lib/kernel/special_form.rs +++ b/libs/@local/hashql/core/src/module/std_lib/kernel/special_form.rs @@ -1,8 +1,10 @@ +use core::alloc::Allocator; + use crate::{ module::{ item::IntrinsicValueItem, locals::TypeDef, - std_lib::{ItemDef, ModuleDef, StandardLibrary, StandardLibraryModule}, + std_lib::{ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule}, }, symbol::{Symbol, sym}, }; @@ -10,9 +12,9 @@ use crate::{ pub(in crate::module::std_lib) struct SpecialForm; impl SpecialForm { - fn make<'heap>( - lib: &StandardLibrary<'_, 'heap>, - def: &mut ModuleDef<'heap>, + fn make<'heap, S: Allocator>( + context: &StandardLibraryContext<'_, 'heap, S>, + def: &mut ModuleDef<'heap, S>, path: Symbol<'heap>, names: impl IntoIterator>, @@ -20,8 +22,8 @@ impl SpecialForm { let value = IntrinsicValueItem { name: path, r#type: TypeDef { - id: lib.ty.never(), - arguments: lib.ty.env.intern_generic_argument_references(&[]), + id: context.ty.never(), + arguments: context.ty.env.intern_generic_argument_references(&[]), }, }; @@ -36,25 +38,28 @@ impl<'heap> StandardLibraryModule<'heap> for SpecialForm { sym::special_form } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); - Self::make(lib, &mut def, sym::path::r#if, [sym::r#if]); - Self::make(lib, &mut def, sym::path::r#as, [sym::r#as]); - Self::make(lib, &mut def, sym::path::r#let, [sym::r#let]); - Self::make(lib, &mut def, sym::path::r#type, [sym::r#type]); - Self::make(lib, &mut def, sym::path::newtype, [sym::newtype]); - Self::make(lib, &mut def, sym::path::r#use, [sym::r#use]); - Self::make(lib, &mut def, sym::path::r#fn, [sym::r#fn]); - Self::make(lib, &mut def, sym::path::input, [sym::input]); + Self::make(context, &mut def, sym::path::r#if, [sym::r#if]); + Self::make(context, &mut def, sym::path::r#as, [sym::r#as]); + Self::make(context, &mut def, sym::path::r#let, [sym::r#let]); + Self::make(context, &mut def, sym::path::r#type, [sym::r#type]); + Self::make(context, &mut def, sym::path::newtype, [sym::newtype]); + Self::make(context, &mut def, sym::path::r#use, [sym::r#use]); + Self::make(context, &mut def, sym::path::r#fn, [sym::r#fn]); + Self::make(context, &mut def, sym::path::input, [sym::input]); Self::make( - lib, + context, &mut def, sym::path::access, [sym::access, sym::symbol::dot], ); Self::make( - lib, + context, &mut def, sym::path::index, [sym::index, sym::symbol::brackets], diff --git a/libs/@local/hashql/core/src/module/std_lib/kernel/type.rs b/libs/@local/hashql/core/src/module/std_lib/kernel/type.rs index c7ffaf4cb3f..6187f0124bc 100644 --- a/libs/@local/hashql/core/src/module/std_lib/kernel/type.rs +++ b/libs/@local/hashql/core/src/module/std_lib/kernel/type.rs @@ -1,7 +1,9 @@ +use core::alloc::Allocator; + use crate::{ module::{ item::IntrinsicTypeItem, - std_lib::{ItemDef, ModuleDef, StandardLibrary, StandardLibraryModule}, + std_lib::{ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule}, }, symbol::{Symbol, sym}, r#type::TypeId, @@ -10,18 +12,18 @@ use crate::{ pub(in crate::module::std_lib) struct Type; impl Type { - fn primitive<'heap>( - lib: &StandardLibrary<'_, 'heap>, - def: &mut ModuleDef<'heap>, + fn primitive<'heap, S: Allocator>( + context: &StandardLibraryContext<'_, 'heap, S>, + def: &mut ModuleDef<'heap, S>, name: Symbol<'heap>, id: TypeId, ) -> usize { - let item = ItemDef::r#type(lib.ty.env, id, &[]); + let item = ItemDef::r#type(context.ty.env, id, &[]); def.push(name, item) } - fn intrinsic<'heap>( - def: &mut ModuleDef<'heap>, + fn intrinsic<'heap, S: Allocator>( + def: &mut ModuleDef<'heap, S>, path: Symbol<'heap>, ident: Symbol<'heap>, ) -> usize { @@ -38,20 +40,23 @@ impl<'heap> StandardLibraryModule<'heap> for Type { sym::r#type } - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap> { - let mut def = ModuleDef::new(); + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S> { + let mut def = ModuleDef::new_in(context.alloc.clone()); - Self::primitive(lib, &mut def, sym::Boolean, lib.ty.boolean()); - Self::primitive(lib, &mut def, sym::Null, lib.ty.null()); - Self::primitive(lib, &mut def, sym::Number, lib.ty.number()); - Self::primitive(lib, &mut def, sym::Integer, lib.ty.integer()); + Self::primitive(context, &mut def, sym::Boolean, context.ty.boolean()); + Self::primitive(context, &mut def, sym::Null, context.ty.null()); + Self::primitive(context, &mut def, sym::Number, context.ty.number()); + Self::primitive(context, &mut def, sym::Integer, context.ty.integer()); // Natural does not yet exist, due to lack of support for refinements - Self::primitive(lib, &mut def, sym::String, lib.ty.string()); + Self::primitive(context, &mut def, sym::String, context.ty.string()); - let unknown = Self::primitive(lib, &mut def, sym::Unknown, lib.ty.unknown()); + let unknown = Self::primitive(context, &mut def, sym::Unknown, context.ty.unknown()); def.alias(unknown, sym::symbol::question_mark); - let never = Self::primitive(lib, &mut def, sym::Never, lib.ty.never()); + let never = Self::primitive(context, &mut def, sym::Never, context.ty.never()); def.alias(never, sym::symbol::exclamation); // Struct/Tuple are purposefully excluded, as they are diff --git a/libs/@local/hashql/core/src/module/std_lib/mod.rs b/libs/@local/hashql/core/src/module/std_lib/mod.rs index 5d126044526..41859a2dbb0 100644 --- a/libs/@local/hashql/core/src/module/std_lib/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/mod.rs @@ -2,19 +2,16 @@ pub mod core; pub mod graph; mod kernel; -use std::alloc::Allocator; - -use ::core::{iter, mem::MaybeUninit, num::NonZero}; +use ::core::{alloc::Allocator, any, iter, mem::MaybeUninit, num::NonZero}; use super::{ModuleId, ModuleRegistry, item::IntrinsicItem, locals::TypeDef}; use crate::{ - collections::SmallVec, - id::{Id, bit_vec::DenseBitSet}, + id::{Id as _, bit_vec::DenseBitSet}, module::{ PartialModule, item::{ConstructorItem, Item, ItemKind}, }, - symbol::{Symbol, sym::newtype}, + symbol::Symbol, r#type::{ TypeBuilder, TypeId, environment::{Environment, instantiate::InstantiateEnvironment}, @@ -143,139 +140,97 @@ hashql_macros::define_id! { struct CacheId(u8 is 0..=u8::MAX) } +struct StandardLibraryContext<'env, 'heap, S: Allocator> { + pub instantiate: InstantiateEnvironment<'env, 'heap>, + pub registry: &'env ModuleRegistry<'heap>, + pub ty: TypeBuilder<'env, 'heap>, + pub alloc: S, +} + struct ModuleCache<'heap, S: Allocator> { - entries: Option>], S>>, + entries: Box<[MaybeUninit>], S>, + lookup: Box<[any::TypeId], S>, + occupied: DenseBitSet, } #[expect(unsafe_code)] impl<'heap, S: Allocator> ModuleCache<'heap, S> { - fn new(length: usize, alloc: S) -> Self { + fn new_in(lookup: Box<[any::TypeId], S>, alloc: S) -> Self + where + S: Clone, + { + let length = lookup.len(); + Self { - entries: Some(Box::new_uninit_slice_in(length, alloc)), + entries: Box::new_uninit_slice_in(length, alloc), + lookup, occupied: DenseBitSet::new_empty(length), } } - fn take(&mut self) -> Option], S>> { - let Some(entries) = self.entries.take() else { - panic!("cache has not been initialized") - }; - - if self.occupied.count() != entries.len() { - self.entries = Some(entries); - return None; - } - - // SAFETY: we know the entry is initialized because we checked `occupied` above - Some(unsafe { entries.assume_init() }) + fn type_to_cache_id(&self) -> CacheId { + self.lookup + .iter() + .position(|&id| id == any::TypeId::of::()) + .map_or_else( + || unreachable!("type not registered in lookup table"), + CacheId::from_usize, + ) } - fn insert(&mut self, id: CacheId, module: ModuleDef<'heap, S>) -> bool { - let Some(entries) = &mut self.entries else { - panic!("cache has not been initialized") - }; - - if self.occupied.contains(id) { - return false; - } + fn insert_unique( + &mut self, + id: CacheId, + module: ModuleDef<'heap, S>, + ) -> &mut ModuleDef<'heap, S> { + assert!(!self.occupied.contains(id), "cache entry already occupied"); - entries[id.as_usize()].write(module); + let value = self.entries[id.as_usize()].write(module); // We only insert AFTER the entry has been initialized because otherwise we could drop a // value on panic that hasn't been initialized. self.occupied.insert(id); - true - } - - fn get(&self, id: CacheId) -> Option<&ModuleDef<'heap, S>> { - let Some(entries) = &self.entries else { - return None; - }; - - if !self.occupied.contains(id) { - return None; - } - - // SAFETY: we know the entry is initialized because we checked `occupied` above - Some(unsafe { entries[id.as_usize()].assume_init_ref() }) - } -} - -#[expect(unsafe_code)] -impl<'heap, S: Allocator> Drop for ModuleCache<'heap, S> { - fn drop(&mut self) { - let Some(mut entries) = self.entries.take() else { - // entries have already been taken from the cache - return; - }; - if self.occupied.count() == entries.len() { - // SAFETY: all the elements have been initialized - unsafe { entries.assume_init_drop() }; - } else { - for index in &self.occupied { - // SAFETY: the element has been initialized by the cache - unsafe { entries[index.as_usize()].assume_init_drop() }; - } - } + value } -} - -pub(super) struct StandardLibrary<'env, 'heap, S: Allocator> { - instantiate: InstantiateEnvironment<'env, 'heap>, - registry: &'env ModuleRegistry<'heap>, - ty: TypeBuilder<'env, 'heap>, - modules: ModuleCache<'heap, S>, -} -impl<'env, 'heap> StandardLibrary<'env, 'heap> { - pub(super) fn new( - environment: &'env Environment<'heap>, - registry: &'env ModuleRegistry<'heap>, - ) -> Self { - Self { - instantiate: InstantiateEnvironment::new(environment), - registry, - ty: TypeBuilder::synthetic(environment), - modules: SmallVec::new(), - } + fn contains(&self, id: CacheId) -> bool { + self.occupied.contains(id) } - fn define_cached(&mut self) -> usize + fn request>( + &mut self, + context: &mut StandardLibraryContext<'_, 'heap, S>, + ) -> &ModuleDef<'heap, S> where - M: StandardLibraryModule<'heap>, + S: Clone, { - let module_id = ::core::any::TypeId::of::(); - if let Some(position) = self.modules.iter().position(|&(id, _)| id == module_id) { - position - } else { - let contents = M::define(self); - - let position = self.modules.len(); - self.modules.push((module_id, contents)); + let id = self.type_to_cache_id::(); - position + if self.contains(id) { + // SAFETY: contains ensures that the entry is initialized + return unsafe { self.entries[id.as_usize()].assume_init_ref() }; } - } - fn manifest(&mut self) -> &ModuleDef<'heap> - where - M: StandardLibraryModule<'heap>, - { - let index = self.define_cached::(); - - &self.modules[index].1 + let module = T::define(context, self); + self.insert_unique(id, module) } - fn build(&mut self, depth: NonZero, parent: ModuleId) -> ModuleId + fn build( + &mut self, + context: &mut StandardLibraryContext<'_, 'heap, S>, + depth: NonZero, + parent: ModuleId, + ) -> ModuleId where - M: StandardLibraryModule<'heap>, + T: StandardLibraryModule<'heap>, + S: Clone, { - self.registry.intern_module(|id| { - let items = &self.manifest::().0; - - let mut output = SmallVec::with_capacity(items.capacity() + M::Children::LENGTH); + context.registry.intern_module(|id| { + let items = &self.request::(context).0; + let mut output = + Vec::with_capacity_in(items.len() + T::Children::LENGTH, context.alloc.clone()); for &ModuleEntry { name, def: kind } in items { let items = match kind { @@ -297,8 +252,9 @@ impl<'env, 'heap> StandardLibrary<'env, 'heap> { } // create all the child modules - let children_names = M::Children::names(); - let children_modules = M::Children::modules(self, depth.saturating_add(1), id.value()); + let children_names = T::Children::names(); + let children_modules = + T::Children::modules(context, self, depth.saturating_add(1), id.value()); for (name, module) in children_names.into_iter().zip(children_modules) { output.push(Item { @@ -309,49 +265,113 @@ impl<'env, 'heap> StandardLibrary<'env, 'heap> { } PartialModule { - name: M::name(), + name: T::name(), parent, depth, - items: self.registry.intern_items(&output), + items: context.registry.intern_items(&output), } }) } +} + +#[expect(unsafe_code)] +impl Drop for ModuleCache<'_, S> { + fn drop(&mut self) { + if self.occupied.count() == self.entries.len() { + // SAFETY: all the elements have been initialized + unsafe { + self.entries.assume_init_drop(); + } + } else { + for index in &self.occupied { + // SAFETY: the element has been initialized by the cache + unsafe { + self.entries[index.as_usize()].assume_init_drop(); + } + } + } + } +} - pub(super) fn register(&mut self) { +pub(super) struct StandardLibrary<'env, 'heap, S: Allocator> { + context: StandardLibraryContext<'env, 'heap, S>, +} + +impl<'env, 'heap, S: Allocator> StandardLibrary<'env, 'heap, S> { + pub(super) fn new( + environment: &'env Environment<'heap>, + registry: &'env ModuleRegistry<'heap>, + alloc: S, + ) -> Self { + Self { + context: StandardLibraryContext { + registry, + instantiate: InstantiateEnvironment::new(environment), + ty: TypeBuilder::synthetic(environment), + alloc, + }, + } + } + + #[expect(unsafe_code)] + pub(super) fn register(&mut self) + where + S: Clone, + { type Root = (self::core::Core, self::kernel::Kernel, self::graph::Graph); const ONE: NonZero = NonZero::new(1).unwrap(); - let roots: smallvec::SmallVec<_, 3> = Root::modules(self, ONE, ModuleId::ROOT) - .into_iter() - .collect(); + let alloc = self.context.alloc.clone(); + let registry = self.context.registry; - for id in roots { - self.registry.register(id); + let count = Root::ITEMS; + + let mut lookup = Box::new_uninit_slice_in(count, alloc.clone()); + lookup.write_filled(any::TypeId::of::()); + // SAFETY: We have just written `count` elements to `lookup`, so it is initialized. + let mut lookup = unsafe { lookup.assume_init() }; + + let mut cursor = 0; + + Root::register(&mut cursor, &mut lookup); + debug_assert_eq!(cursor, count); + + let mut cache = ModuleCache::new_in(lookup, alloc); + for module in Root::modules(&mut self.context, &mut cache, ONE, ModuleId::ROOT) { + registry.register(module); } } } trait Submodules<'heap> { const LENGTH: usize; + const ITEMS: usize; fn names() -> impl IntoIterator>; - fn modules( - lib: &mut StandardLibrary<'_, 'heap>, + fn register(cursor: &mut usize, items: &mut [any::TypeId]); + + fn modules( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, depth: NonZero, parent: ModuleId, ) -> impl IntoIterator; } impl<'heap> Submodules<'heap> for () { + const ITEMS: usize = 0; const LENGTH: usize = 0; fn names() -> impl IntoIterator> { iter::empty() } - fn modules( - _: &mut StandardLibrary<'_, 'heap>, + fn register(_: &mut usize, _: &mut [any::TypeId]) {} + + fn modules( + _: &mut StandardLibraryContext<'_, 'heap, S>, + _: &mut ModuleCache<'heap, S>, _: NonZero, _: ModuleId, ) -> impl IntoIterator { @@ -378,6 +398,16 @@ macro_rules! impl_submodules { $($item: StandardLibraryModule<'heap>,)* { const LENGTH: usize = ${count($item)}; + const ITEMS: usize = ${count($item)} $(+ <$item::Children as Submodules<'heap>>::ITEMS)*; + + fn register(cursor: &mut usize, items: &mut [any::TypeId]) { + $( + items[*cursor] = any::TypeId::of::<$item>(); + *cursor += 1; + + <$item::Children as Submodules<'heap>>::register(cursor, items); + )* + } fn names() -> impl IntoIterator> { $(let $item = $item::name();)* @@ -385,12 +415,13 @@ macro_rules! impl_submodules { [$($item),*] } - fn modules( - lib: &mut StandardLibrary<'_, 'heap>, + fn modules( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, depth: NonZero, parent: ModuleId, ) -> impl IntoIterator { - $(let $item = lib.build::<$item>(depth, parent);)* + $(let $item = cache.build::<$item>(context, depth, parent);)* [$($item),*] } @@ -405,7 +436,10 @@ trait StandardLibraryModule<'heap>: 'static { fn name() -> Symbol<'heap>; - fn define(lib: &mut StandardLibrary<'_, 'heap>) -> ModuleDef<'heap>; + fn define( + context: &mut StandardLibraryContext<'_, 'heap, S>, + cache: &mut ModuleCache<'heap, S>, + ) -> ModuleDef<'heap, S>; } /// Declares a generic function type with parameters and return type. From f4d4e9a43a82e124f4cd360c8121de747dd68337 Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud <7252775+indietyp@users.noreply.github.com> Date: Fri, 19 Jun 2026 14:35:45 +0200 Subject: [PATCH 05/12] chore: lints --- libs/@local/hashql/core/src/module/std_lib/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/@local/hashql/core/src/module/std_lib/mod.rs b/libs/@local/hashql/core/src/module/std_lib/mod.rs index 41859a2dbb0..01a50e10068 100644 --- a/libs/@local/hashql/core/src/module/std_lib/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/mod.rs @@ -76,8 +76,8 @@ impl<'heap> ModuleEntry<'heap> { struct ModuleDef<'heap, S: Allocator>(Vec, S>); impl<'heap, S: Allocator> ModuleDef<'heap, S> { - const fn new_in(alloc: S) -> Self { - Self(Vec::new_in(alloc)) + fn new_in(alloc: S) -> Self { + Self(Vec::with_capacity_in(16, alloc)) } fn push(&mut self, name: Symbol<'heap>, def: ItemDef<'heap>) -> usize { From 939afa609acaa1fdf61e3d416e5d2d0d9acfa9a4 Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud <7252775+indietyp@users.noreply.github.com> Date: Fri, 19 Jun 2026 15:14:47 +0200 Subject: [PATCH 06/12] feat: speedup --- libs/@local/hashql/core/src/lib.rs | 1 - .../core/src/module/std_lib/core/bits.rs | 6 +- .../core/src/module/std_lib/core/bool.rs | 6 +- .../core/src/module/std_lib/core/cmp.rs | 6 +- .../core/src/module/std_lib/core/json.rs | 4 +- .../core/src/module/std_lib/core/math.rs | 6 +- .../core/src/module/std_lib/core/mod.rs | 6 +- .../core/src/module/std_lib/core/option.rs | 4 +- .../core/src/module/std_lib/core/result.rs | 4 +- .../core/src/module/std_lib/core/url.rs | 4 +- .../core/src/module/std_lib/core/uuid.rs | 4 +- .../core/src/module/std_lib/graph/body.rs | 4 +- .../core/src/module/std_lib/graph/entity.rs | 4 +- .../core/src/module/std_lib/graph/head.rs | 4 +- .../core/src/module/std_lib/graph/mod.rs | 4 +- .../core/src/module/std_lib/graph/tail.rs | 4 +- .../core/src/module/std_lib/graph/temporal.rs | 4 +- .../core/src/module/std_lib/graph/tmp.rs | 8 +- .../std_lib/graph/types/knowledge/entity.rs | 5 +- .../std_lib/graph/types/knowledge/mod.rs | 6 +- .../src/module/std_lib/graph/types/mod.rs | 6 +- .../graph/types/ontology/entity_type.rs | 6 +- .../std_lib/graph/types/ontology/mod.rs | 5 +- .../graph/types/principal/actor_group/mod.rs | 5 +- .../graph/types/principal/actor_group/web.rs | 5 +- .../std_lib/graph/types/principal/mod.rs | 6 +- .../core/src/module/std_lib/kernel/mod.rs | 4 +- .../src/module/std_lib/kernel/special_form.rs | 9 +- .../core/src/module/std_lib/kernel/type.rs | 6 +- .../hashql/core/src/module/std_lib/mod.rs | 264 ++++++++++-------- libs/darwin-kperf/criterion/src/pmc.rs | 3 + 31 files changed, 260 insertions(+), 153 deletions(-) diff --git a/libs/@local/hashql/core/src/lib.rs b/libs/@local/hashql/core/src/lib.rs index daf94f95779..11ea5e65acc 100644 --- a/libs/@local/hashql/core/src/lib.rs +++ b/libs/@local/hashql/core/src/lib.rs @@ -39,7 +39,6 @@ sync_nonpoison, try_trait_v2, variant_count, - maybe_uninit_fill )] extern crate alloc; diff --git a/libs/@local/hashql/core/src/module/std_lib/core/bits.rs b/libs/@local/hashql/core/src/module/std_lib/core/bits.rs index 9df38c2e2ff..37a1378ea1f 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/bits.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/bits.rs @@ -4,7 +4,9 @@ use super::func; use crate::{ module::{ locals::TypeDef, - std_lib::{ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, decl}, + std_lib::{ + CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, decl, + }, }, symbol::{Symbol, sym}, }; @@ -16,6 +18,8 @@ pub(in crate::module::std_lib) struct Bits { impl<'heap> StandardLibraryModule<'heap> for Bits { type Children = (); + const CACHE_ID: CacheId = CacheId::CoreBits; + fn name() -> Symbol<'heap> { sym::bits } diff --git a/libs/@local/hashql/core/src/module/std_lib/core/bool.rs b/libs/@local/hashql/core/src/module/std_lib/core/bool.rs index 0be4dbc177c..ba388f2e19a 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/bool.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/bool.rs @@ -4,7 +4,9 @@ use super::func; use crate::{ module::{ locals::TypeDef, - std_lib::{ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, decl}, + std_lib::{ + CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, decl, + }, }, symbol::{Symbol, sym}, }; @@ -16,6 +18,8 @@ pub(in crate::module::std_lib) struct Bool { impl<'heap> StandardLibraryModule<'heap> for Bool { type Children = (); + const CACHE_ID: CacheId = CacheId::CoreBool; + fn name() -> Symbol<'heap> { sym::bool } diff --git a/libs/@local/hashql/core/src/module/std_lib/core/cmp.rs b/libs/@local/hashql/core/src/module/std_lib/core/cmp.rs index ed1bd68a242..614d9a72957 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/cmp.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/cmp.rs @@ -4,7 +4,9 @@ use super::func; use crate::{ module::{ locals::TypeDef, - std_lib::{ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, decl}, + std_lib::{ + CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, decl, + }, }, symbol::{Symbol, sym}, }; @@ -16,6 +18,8 @@ pub(in crate::module::std_lib) struct Cmp { impl<'heap> StandardLibraryModule<'heap> for Cmp { type Children = (); + const CACHE_ID: CacheId = CacheId::CoreCmp; + fn name() -> Symbol<'heap> { sym::cmp } diff --git a/libs/@local/hashql/core/src/module/std_lib/core/json.rs b/libs/@local/hashql/core/src/module/std_lib/core/json.rs index 852657487eb..741ff60b20d 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/json.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/json.rs @@ -2,7 +2,7 @@ use core::alloc::Allocator; use crate::{ module::std_lib::{ - ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, }, symbol::{Symbol, sym}, }; @@ -14,6 +14,8 @@ pub(in crate::module::std_lib) struct Json { impl<'heap> StandardLibraryModule<'heap> for Json { type Children = (); + const CACHE_ID: CacheId = CacheId::CoreJson; + fn name() -> Symbol<'heap> { sym::json } diff --git a/libs/@local/hashql/core/src/module/std_lib/core/math.rs b/libs/@local/hashql/core/src/module/std_lib/core/math.rs index 0ea55511105..f4e25bed6ed 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/math.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/math.rs @@ -4,7 +4,9 @@ use super::func; use crate::{ module::{ locals::TypeDef, - std_lib::{ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, decl}, + std_lib::{ + CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, decl, + }, }, symbol::{Symbol, sym}, }; @@ -16,6 +18,8 @@ pub(in crate::module::std_lib) struct Math { impl<'heap> StandardLibraryModule<'heap> for Math { type Children = (); + const CACHE_ID: CacheId = CacheId::CoreMath; + fn name() -> Symbol<'heap> { sym::math } diff --git a/libs/@local/hashql/core/src/module/std_lib/core/mod.rs b/libs/@local/hashql/core/src/module/std_lib/core/mod.rs index e85015b822d..1c89a5fce56 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/mod.rs @@ -1,6 +1,8 @@ use core::alloc::Allocator; -use super::{ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule}; +use super::{ + CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, +}; use crate::{ module::{item::IntrinsicValueItem, locals::TypeDef}, symbol::{Symbol, sym}, @@ -44,6 +46,8 @@ impl<'heap> StandardLibraryModule<'heap> for Core { self::uuid::Uuid, ); + const CACHE_ID: CacheId = CacheId::Core; + fn name() -> Symbol<'heap> { sym::core } diff --git a/libs/@local/hashql/core/src/module/std_lib/core/option.rs b/libs/@local/hashql/core/src/module/std_lib/core/option.rs index 41cc28e38aa..7e0c1e687b9 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/option.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/option.rs @@ -2,7 +2,7 @@ use core::alloc::Allocator; use crate::{ module::std_lib::{ - ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, }, symbol::{Symbol, sym}, }; @@ -30,6 +30,8 @@ pub(in crate::module::std_lib) struct Option { impl<'heap> StandardLibraryModule<'heap> for Option { type Children = (); + const CACHE_ID: CacheId = CacheId::CoreOption; + fn name() -> Symbol<'heap> { sym::option } diff --git a/libs/@local/hashql/core/src/module/std_lib/core/result.rs b/libs/@local/hashql/core/src/module/std_lib/core/result.rs index f02f76549a1..b337b6129d7 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/result.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/result.rs @@ -2,7 +2,7 @@ use core::alloc::Allocator; use crate::{ module::std_lib::{ - ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, }, symbol::{Symbol, sym}, }; @@ -14,6 +14,8 @@ pub(in crate::module::std_lib) struct Result { impl<'heap> StandardLibraryModule<'heap> for Result { type Children = (); + const CACHE_ID: CacheId = CacheId::CoreResult; + fn name() -> Symbol<'heap> { sym::result } diff --git a/libs/@local/hashql/core/src/module/std_lib/core/url.rs b/libs/@local/hashql/core/src/module/std_lib/core/url.rs index 2b7d2bbf7eb..6d10f2a9bed 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/url.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/url.rs @@ -2,7 +2,7 @@ use core::alloc::Allocator; use crate::{ module::std_lib::{ - ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, }, symbol::{Symbol, sym}, }; @@ -25,6 +25,8 @@ pub(in crate::module::std_lib) struct Url { impl<'heap> StandardLibraryModule<'heap> for Url { type Children = (); + const CACHE_ID: CacheId = CacheId::CoreUrl; + fn name() -> Symbol<'heap> { sym::url } diff --git a/libs/@local/hashql/core/src/module/std_lib/core/uuid.rs b/libs/@local/hashql/core/src/module/std_lib/core/uuid.rs index d3e80be57ac..1be4967497b 100644 --- a/libs/@local/hashql/core/src/module/std_lib/core/uuid.rs +++ b/libs/@local/hashql/core/src/module/std_lib/core/uuid.rs @@ -2,7 +2,7 @@ use core::alloc::Allocator; use crate::{ module::std_lib::{ - ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, }, symbol::{Symbol, sym}, }; @@ -25,6 +25,8 @@ pub(in crate::module::std_lib) struct Uuid { impl<'heap> StandardLibraryModule<'heap> for Uuid { type Children = (); + const CACHE_ID: CacheId = CacheId::CoreUuid; + fn name() -> Symbol<'heap> { sym::uuid } diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/body.rs b/libs/@local/hashql/core/src/module/std_lib/graph/body.rs index c2176f8f7ec..7978eabf5f9 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/body.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/body.rs @@ -4,7 +4,7 @@ use crate::{ module::{ locals::TypeDef, std_lib::{ - self, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + self, CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, core::func, decl, }, }, @@ -18,6 +18,8 @@ pub(in crate::module::std_lib) struct Body { impl<'heap> StandardLibraryModule<'heap> for Body { type Children = (); + const CACHE_ID: CacheId = CacheId::GraphBody; + fn name() -> Symbol<'heap> { sym::body } diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/entity.rs b/libs/@local/hashql/core/src/module/std_lib/graph/entity.rs index 90b1515a551..0109d5c70b7 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/entity.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/entity.rs @@ -4,7 +4,7 @@ use crate::{ module::{ locals::TypeDef, std_lib::{ - self, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + self, CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, core::{func, option::types::option}, decl, }, @@ -22,6 +22,8 @@ pub(in crate::module::std_lib) struct Entity { impl<'heap> StandardLibraryModule<'heap> for Entity { type Children = (); + const CACHE_ID: CacheId = CacheId::GraphEntity; + fn name() -> Symbol<'heap> { sym::entity } diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/head.rs b/libs/@local/hashql/core/src/module/std_lib/graph/head.rs index 60fcf253ba2..c2085e0405a 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/head.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/head.rs @@ -4,7 +4,7 @@ use crate::{ module::{ locals::TypeDef, std_lib::{ - self, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + self, CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, core::func, decl, }, }, @@ -22,6 +22,8 @@ pub(in crate::module::std_lib) struct Head { impl<'heap> StandardLibraryModule<'heap> for Head { type Children = (); + const CACHE_ID: CacheId = CacheId::GraphHead; + fn name() -> Symbol<'heap> { sym::head } diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/mod.rs b/libs/@local/hashql/core/src/module/std_lib/graph/mod.rs index 8a5d74dac82..98cbc327ee6 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/mod.rs @@ -2,7 +2,7 @@ use core::alloc::Allocator; use crate::{ module::std_lib::{ - ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, }, symbol::{Symbol, sym}, }; @@ -30,6 +30,8 @@ impl<'heap> StandardLibraryModule<'heap> for Graph { self::types::Types, ); + const CACHE_ID: CacheId = CacheId::Graph; + fn name() -> Symbol<'heap> { sym::graph } diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/tail.rs b/libs/@local/hashql/core/src/module/std_lib/graph/tail.rs index a38b51317c6..ef4fcb7f39c 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/tail.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/tail.rs @@ -4,7 +4,7 @@ use crate::{ module::{ locals::TypeDef, std_lib::{ - self, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + self, CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, core::func, decl, }, }, @@ -18,6 +18,8 @@ pub(in crate::module::std_lib) struct Tail { impl<'heap> StandardLibraryModule<'heap> for Tail { type Children = (); + const CACHE_ID: CacheId = CacheId::GraphTail; + fn name() -> Symbol<'heap> { sym::tail } diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/temporal.rs b/libs/@local/hashql/core/src/module/std_lib/graph/temporal.rs index 490518fe66d..d98c9730a92 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/temporal.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/temporal.rs @@ -2,7 +2,7 @@ use core::alloc::Allocator; use crate::{ module::std_lib::{ - ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, }, symbol::{Symbol, sym}, }; @@ -112,6 +112,8 @@ pub(in crate::module::std_lib) struct Temporal { impl<'heap> StandardLibraryModule<'heap> for Temporal { type Children = (); + const CACHE_ID: CacheId = CacheId::GraphTemporal; + fn name() -> Symbol<'heap> { sym::temporal } diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/tmp.rs b/libs/@local/hashql/core/src/module/std_lib/graph/tmp.rs index 02d5aa61197..3d2fca7edf8 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/tmp.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/tmp.rs @@ -1,10 +1,12 @@ use core::alloc::Allocator; use crate::{ + intern::Interned, module::{ locals::TypeDef, std_lib::{ - self, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, core::func, + self, CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + core::func, }, }, symbol::{Symbol, sym}, @@ -18,6 +20,8 @@ pub(in crate::module::std_lib) struct Tmp { impl<'heap> StandardLibraryModule<'heap> for Tmp { type Children = (); + const CACHE_ID: CacheId = CacheId::GraphTmp; + fn name() -> Symbol<'heap> { sym::tmp } @@ -41,7 +45,7 @@ impl<'heap> StandardLibraryModule<'heap> for Tmp { id: context .ty .closure([] as [TypeId; 0], query_temporal_axes_ty.id), - arguments: context.ty.env.intern_generic_argument_references(&[]), + arguments: Interned::empty(), }, ); diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/entity.rs b/libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/entity.rs index c2952938587..35b8eb2fe15 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/entity.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/entity.rs @@ -2,7 +2,8 @@ use core::alloc::Allocator; use crate::{ module::std_lib::{ - self, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + self, CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, + StandardLibraryModule, }, symbol::{Symbol, sym}, }; @@ -373,6 +374,8 @@ pub(in crate::module::std_lib) struct Entity { impl<'heap> StandardLibraryModule<'heap> for Entity { type Children = (); + const CACHE_ID: CacheId = CacheId::GraphTypesKnowledgeEntity; + fn name() -> Symbol<'heap> { sym::entity } diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/mod.rs b/libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/mod.rs index 115bdd89c9a..b3994b6221d 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/types/knowledge/mod.rs @@ -3,7 +3,9 @@ use core::alloc::Allocator; pub mod entity; use crate::{ - module::std_lib::{ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule}, + module::std_lib::{ + CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + }, symbol::{Symbol, sym}, }; @@ -12,6 +14,8 @@ pub(in crate::module::std_lib) struct Knowledge; impl<'heap> StandardLibraryModule<'heap> for Knowledge { type Children = (self::entity::Entity,); + const CACHE_ID: CacheId = CacheId::GraphTypesKnowledge; + fn name() -> Symbol<'heap> { sym::knowledge } diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/types/mod.rs b/libs/@local/hashql/core/src/module/std_lib/graph/types/mod.rs index 0e01e828215..65c28b35a51 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/types/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/types/mod.rs @@ -2,7 +2,9 @@ use core::alloc::Allocator; // This is in a separate module, to facilitate: https://linear.app/hash/issue/H-4735/hashql-convert-rust-types-into-hashql-types use crate::{ - module::std_lib::{ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule}, + module::std_lib::{ + CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + }, symbol::{Symbol, sym}, }; @@ -21,6 +23,8 @@ impl<'heap> StandardLibraryModule<'heap> for Types { self::principal::Principal, ); + const CACHE_ID: CacheId = CacheId::GraphTypes; + fn name() -> Symbol<'heap> { sym::types } diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/entity_type.rs b/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/entity_type.rs index fe4afe57e85..0f6cccd9d31 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/entity_type.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/entity_type.rs @@ -2,8 +2,8 @@ use core::alloc::Allocator; use crate::{ module::std_lib::{ - self, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, - core::option::types::option, + self, CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, + StandardLibraryModule, core::option::types::option, }, symbol::{Symbol, sym}, }; @@ -18,6 +18,8 @@ pub(in crate::module::std_lib) struct EntityType { impl<'heap> StandardLibraryModule<'heap> for EntityType { type Children = (); + const CACHE_ID: CacheId = CacheId::GraphTypesOntologyEntityType; + fn name() -> Symbol<'heap> { sym::entity_type } diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/mod.rs b/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/mod.rs index 69e9f3ecabe..1e17850802c 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/types/ontology/mod.rs @@ -2,7 +2,8 @@ use core::alloc::Allocator; use crate::{ module::std_lib::{ - self, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + self, CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, + StandardLibraryModule, }, symbol::{Symbol, sym}, }; @@ -66,6 +67,8 @@ pub(in crate::module::std_lib) struct Ontology { impl<'heap> StandardLibraryModule<'heap> for Ontology { type Children = (self::entity_type::EntityType,); + const CACHE_ID: CacheId = CacheId::GraphTypesOntology; + fn name() -> Symbol<'heap> { sym::ontology } diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/mod.rs b/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/mod.rs index 480b99545e7..e03fb858cad 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/mod.rs @@ -2,7 +2,8 @@ use core::alloc::Allocator; use crate::{ module::std_lib::{ - self, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + self, CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, + StandardLibraryModule, }, symbol::{Symbol, sym}, }; @@ -41,6 +42,8 @@ pub(in crate::module::std_lib) struct ActorGroup { impl<'heap> StandardLibraryModule<'heap> for ActorGroup { type Children = (self::web::Web,); + const CACHE_ID: CacheId = CacheId::GraphTypesPrincipalActorGroup; + fn name() -> Symbol<'heap> { sym::actor_group } diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/web.rs b/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/web.rs index 02d04353689..bd1096f5a14 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/web.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/actor_group/web.rs @@ -2,7 +2,8 @@ use core::alloc::Allocator; use crate::{ module::std_lib::{ - self, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + self, CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, + StandardLibraryModule, }, symbol::{Symbol, sym}, }; @@ -40,6 +41,8 @@ pub(in crate::module::std_lib) struct Web { impl<'heap> StandardLibraryModule<'heap> for Web { type Children = (); + const CACHE_ID: CacheId = CacheId::GraphTypesPrincipalActorGroupWeb; + fn name() -> Symbol<'heap> { sym::web } diff --git a/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/mod.rs b/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/mod.rs index 75bc1936810..02af6e5c0cc 100644 --- a/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/graph/types/principal/mod.rs @@ -3,7 +3,9 @@ use core::alloc::Allocator; pub mod actor_group; use crate::{ - module::std_lib::{ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule}, + module::std_lib::{ + CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + }, symbol::{Symbol, sym}, }; @@ -14,6 +16,8 @@ pub(in crate::module::std_lib) struct Principal { impl<'heap> StandardLibraryModule<'heap> for Principal { type Children = (self::actor_group::ActorGroup,); + const CACHE_ID: CacheId = CacheId::GraphTypesPrincipal; + fn name() -> Symbol<'heap> { sym::principal } diff --git a/libs/@local/hashql/core/src/module/std_lib/kernel/mod.rs b/libs/@local/hashql/core/src/module/std_lib/kernel/mod.rs index 7ca71c5ee75..a39ab7c6483 100644 --- a/libs/@local/hashql/core/src/module/std_lib/kernel/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/kernel/mod.rs @@ -3,7 +3,7 @@ use core::alloc::Allocator; pub(in crate::module::std_lib) mod special_form; pub(in crate::module::std_lib) mod r#type; -use super::{ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule}; +use super::{CacheId, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule}; use crate::symbol::{Symbol, sym}; pub(in crate::module::std_lib) struct Kernel; @@ -11,6 +11,8 @@ pub(in crate::module::std_lib) struct Kernel; impl<'heap> StandardLibraryModule<'heap> for Kernel { type Children = (self::special_form::SpecialForm, self::r#type::Type); + const CACHE_ID: CacheId = CacheId::Kernel; + fn name() -> Symbol<'heap> { sym::kernel } diff --git a/libs/@local/hashql/core/src/module/std_lib/kernel/special_form.rs b/libs/@local/hashql/core/src/module/std_lib/kernel/special_form.rs index 6b175d67f42..adff9ab1d39 100644 --- a/libs/@local/hashql/core/src/module/std_lib/kernel/special_form.rs +++ b/libs/@local/hashql/core/src/module/std_lib/kernel/special_form.rs @@ -1,10 +1,13 @@ use core::alloc::Allocator; use crate::{ + intern::Interned, module::{ item::IntrinsicValueItem, locals::TypeDef, - std_lib::{ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule}, + std_lib::{ + CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + }, }, symbol::{Symbol, sym}, }; @@ -23,7 +26,7 @@ impl SpecialForm { name: path, r#type: TypeDef { id: context.ty.never(), - arguments: context.ty.env.intern_generic_argument_references(&[]), + arguments: Interned::empty(), }, }; @@ -34,6 +37,8 @@ impl SpecialForm { impl<'heap> StandardLibraryModule<'heap> for SpecialForm { type Children = (); + const CACHE_ID: CacheId = CacheId::KernelSpecialForm; + fn name() -> Symbol<'heap> { sym::special_form } diff --git a/libs/@local/hashql/core/src/module/std_lib/kernel/type.rs b/libs/@local/hashql/core/src/module/std_lib/kernel/type.rs index 6187f0124bc..5de86d96115 100644 --- a/libs/@local/hashql/core/src/module/std_lib/kernel/type.rs +++ b/libs/@local/hashql/core/src/module/std_lib/kernel/type.rs @@ -3,7 +3,9 @@ use core::alloc::Allocator; use crate::{ module::{ item::IntrinsicTypeItem, - std_lib::{ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule}, + std_lib::{ + CacheId, ItemDef, ModuleCache, ModuleDef, StandardLibraryContext, StandardLibraryModule, + }, }, symbol::{Symbol, sym}, r#type::TypeId, @@ -36,6 +38,8 @@ impl Type { impl<'heap> StandardLibraryModule<'heap> for Type { type Children = (); + const CACHE_ID: CacheId = CacheId::KernelType; + fn name() -> Symbol<'heap> { sym::r#type } diff --git a/libs/@local/hashql/core/src/module/std_lib/mod.rs b/libs/@local/hashql/core/src/module/std_lib/mod.rs index 01a50e10068..d288ef08b05 100644 --- a/libs/@local/hashql/core/src/module/std_lib/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/mod.rs @@ -2,11 +2,11 @@ pub mod core; pub mod graph; mod kernel; -use ::core::{alloc::Allocator, any, iter, mem::MaybeUninit, num::NonZero}; +use ::core::{alloc::Allocator, iter, mem, mem::MaybeUninit, num::NonZero}; use super::{ModuleId, ModuleRegistry, item::IntrinsicItem, locals::TypeDef}; use crate::{ - id::{Id as _, bit_vec::DenseBitSet}, + id::{Id as _, bit_vec::FiniteBitSet}, module::{ PartialModule, item::{ConstructorItem, Item, ItemKind}, @@ -73,16 +73,27 @@ impl<'heap> ModuleEntry<'heap> { } } -struct ModuleDef<'heap, S: Allocator>(Vec, S>); +struct ModuleDef<'heap, S: Allocator> { + entries: Vec, S>, + /// The number of items that will be emitted when this module is built. + /// + /// This differs from `entries.len()` because `Newtype` entries emit two items (a constructor + /// and a type), while other entries emit one. + emitted_len: usize, +} impl<'heap, S: Allocator> ModuleDef<'heap, S> { fn new_in(alloc: S) -> Self { - Self(Vec::with_capacity_in(16, alloc)) + Self { + entries: Vec::with_capacity_in(16, alloc), + emitted_len: 0, + } } fn push(&mut self, name: Symbol<'heap>, def: ItemDef<'heap>) -> usize { - let index = self.0.len(); - self.0.push(ModuleEntry::new(name, def)); + let index = self.entries.len(); + self.emitted_len += def.emitted_len(); + self.entries.push(ModuleEntry::new(name, def)); index } @@ -94,7 +105,7 @@ impl<'heap, S: Allocator> ModuleDef<'heap, S> { ) { let names = names.into_iter(); - self.0.reserve(names.size_hint().0); + self.entries.reserve(names.size_hint().0); for name in names { self.push(name, def); @@ -102,15 +113,16 @@ impl<'heap, S: Allocator> ModuleDef<'heap, S> { } fn alias(&mut self, index: usize, alias: Symbol<'heap>) -> usize { - let item = self.0[index].alias(alias); + let item = self.entries[index].alias(alias); + self.emitted_len += item.def.emitted_len(); - let index = self.0.len(); - self.0.push(item); + let index = self.entries.len(); + self.entries.push(item); index } fn find(&self, name: Symbol<'heap>) -> Option> { - self.0.iter().find(|item| item.name == name).copied() + self.entries.iter().find(|item| item.name == name).copied() } #[track_caller] @@ -135,9 +147,54 @@ impl<'heap, S: Allocator> ModuleDef<'heap, S> { } } -hashql_macros::define_id! { - #[id(crate = crate)] - struct CacheId(u8 is 0..=u8::MAX) +impl<'heap> ItemDef<'heap> { + /// Returns the number of items emitted when building this definition. + /// + /// `Newtype` emits both a constructor and a type; other variants emit one item. + const fn emitted_len(self) -> usize { + match self { + Self::Newtype(_) => 2, + Self::Type(_) | Self::Intrinsic(_) => 1, + } + } +} + +/// Index into the `ModuleCache` entries array. +/// +/// Variants are listed in preorder traversal of the module tree (parent before children, left +/// before right). Adding a new stdlib module requires adding a variant here in the correct +/// position. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, hashql_macros::Id)] +#[id(const, crate = crate)] +enum CacheId { + Core, + CoreBits, + CoreBool, + CoreCmp, + CoreJson, + CoreMath, + CoreOption, + CoreResult, + CoreUrl, + CoreUuid, + Kernel, + KernelSpecialForm, + KernelType, + Graph, + GraphTemporal, + GraphHead, + GraphBody, + GraphTail, + GraphEntity, + GraphTmp, + GraphTypes, + GraphTypesKnowledge, + GraphTypesKnowledgeEntity, + GraphTypesOntology, + GraphTypesOntologyEntityType, + GraphTypesPrincipal, + GraphTypesPrincipalActorGroup, + GraphTypesPrincipalActorGroupWeb, } struct StandardLibraryContext<'env, 'heap, S: Allocator> { @@ -149,56 +206,22 @@ struct StandardLibraryContext<'env, 'heap, S: Allocator> { struct ModuleCache<'heap, S: Allocator> { entries: Box<[MaybeUninit>], S>, - lookup: Box<[any::TypeId], S>, - - occupied: DenseBitSet, + /// Tracks which cache entries have been initialized. + /// + /// Bit `i` is set when `entries[i]` has been written. The stdlib has fewer than 64 modules, so + /// a `u64`-backed `FiniteBitSet` suffices. + occupied: FiniteBitSet, } #[expect(unsafe_code)] impl<'heap, S: Allocator> ModuleCache<'heap, S> { - fn new_in(lookup: Box<[any::TypeId], S>, alloc: S) -> Self - where - S: Clone, - { - let length = lookup.len(); - + fn new_in(count: usize, alloc: S) -> Self { Self { - entries: Box::new_uninit_slice_in(length, alloc), - lookup, - occupied: DenseBitSet::new_empty(length), + entries: Box::new_uninit_slice_in(count, alloc), + occupied: FiniteBitSet::new_empty(count as u32), } } - fn type_to_cache_id(&self) -> CacheId { - self.lookup - .iter() - .position(|&id| id == any::TypeId::of::()) - .map_or_else( - || unreachable!("type not registered in lookup table"), - CacheId::from_usize, - ) - } - - fn insert_unique( - &mut self, - id: CacheId, - module: ModuleDef<'heap, S>, - ) -> &mut ModuleDef<'heap, S> { - assert!(!self.occupied.contains(id), "cache entry already occupied"); - - let value = self.entries[id.as_usize()].write(module); - - // We only insert AFTER the entry has been initialized because otherwise we could drop a - // value on panic that hasn't been initialized. - self.occupied.insert(id); - - value - } - - fn contains(&self, id: CacheId) -> bool { - self.occupied.contains(id) - } - fn request>( &mut self, context: &mut StandardLibraryContext<'_, 'heap, S>, @@ -206,15 +229,17 @@ impl<'heap, S: Allocator> ModuleCache<'heap, S> { where S: Clone, { - let id = self.type_to_cache_id::(); + let id = T::CACHE_ID; - if self.contains(id) { - // SAFETY: contains ensures that the entry is initialized + if self.occupied.contains(id) { + // SAFETY: the bit is set only after the entry has been initialized. return unsafe { self.entries[id.as_usize()].assume_init_ref() }; } let module = T::define(context, self); - self.insert_unique(id, module) + let value = self.entries[id.as_usize()].write(module); + self.occupied.insert(id); + value } fn build( @@ -228,39 +253,52 @@ impl<'heap, S: Allocator> ModuleCache<'heap, S> { S: Clone, { context.registry.intern_module(|id| { - let items = &self.request::(context).0; + let def = self.request::(context); let mut output = - Vec::with_capacity_in(items.len() + T::Children::LENGTH, context.alloc.clone()); - - for &ModuleEntry { name, def: kind } in items { - let items = match kind { - ItemDef::Intrinsic(intrinsic) => [Some(ItemKind::Intrinsic(intrinsic)), None], - ItemDef::Type(def) => [Some(ItemKind::Type(def)), None], - ItemDef::Newtype(def) => [ - Some(ItemKind::Constructor(ConstructorItem { r#type: def })), - Some(ItemKind::Type(def)), - ], - }; - - for kind in items.into_iter().flatten() { - output.push(Item { - module: id.value(), - name, - kind, - }); + Vec::with_capacity_in(def.emitted_len + T::Children::LENGTH, context.alloc.clone()); + let module = id.value(); + + for &ModuleEntry { name, def } in &def.entries { + match def { + ItemDef::Intrinsic(intrinsic) => { + output.push(Item { + module, + name, + kind: ItemKind::Intrinsic(intrinsic), + }); + } + ItemDef::Type(typedef) => { + output.push(Item { + module, + name, + kind: ItemKind::Type(typedef), + }); + } + ItemDef::Newtype(typedef) => { + output.push(Item { + module, + name, + kind: ItemKind::Constructor(ConstructorItem { r#type: typedef }), + }); + output.push(Item { + module, + name, + kind: ItemKind::Type(typedef), + }); + } } } // create all the child modules let children_names = T::Children::names(); let children_modules = - T::Children::modules(context, self, depth.saturating_add(1), id.value()); + T::Children::modules(context, self, depth.saturating_add(1), module); - for (name, module) in children_names.into_iter().zip(children_modules) { + for (name, child) in children_names.into_iter().zip(children_modules) { output.push(Item { - module: id.value(), + module, name, - kind: ItemKind::Module(module), + kind: ItemKind::Module(child), }); } @@ -277,17 +315,10 @@ impl<'heap, S: Allocator> ModuleCache<'heap, S> { #[expect(unsafe_code)] impl Drop for ModuleCache<'_, S> { fn drop(&mut self) { - if self.occupied.count() == self.entries.len() { - // SAFETY: all the elements have been initialized + for index in &self.occupied { + // SAFETY: the bit is set only after the entry has been initialized. unsafe { - self.entries.assume_init_drop(); - } - } else { - for index in &self.occupied { - // SAFETY: the element has been initialized by the cache - unsafe { - self.entries[index.as_usize()].assume_init_drop(); - } + self.entries[index.as_usize()].assume_init_drop(); } } } @@ -313,30 +344,24 @@ impl<'env, 'heap, S: Allocator> StandardLibrary<'env, 'heap, S> { } } - #[expect(unsafe_code)] pub(super) fn register(&mut self) where S: Clone, { type Root = (self::core::Core, self::kernel::Kernel, self::graph::Graph); const ONE: NonZero = NonZero::new(1).unwrap(); + const MODULE_COUNT: usize = mem::variant_count::(); + + debug_assert_eq!( + Root::ITEMS, + MODULE_COUNT, + "CacheId variant count does not match module tree size", + ); let alloc = self.context.alloc.clone(); let registry = self.context.registry; - let count = Root::ITEMS; - - let mut lookup = Box::new_uninit_slice_in(count, alloc.clone()); - lookup.write_filled(any::TypeId::of::()); - // SAFETY: We have just written `count` elements to `lookup`, so it is initialized. - let mut lookup = unsafe { lookup.assume_init() }; - - let mut cursor = 0; - - Root::register(&mut cursor, &mut lookup); - debug_assert_eq!(cursor, count); - - let mut cache = ModuleCache::new_in(lookup, alloc); + let mut cache = ModuleCache::new_in(MODULE_COUNT, alloc); for module in Root::modules(&mut self.context, &mut cache, ONE, ModuleId::ROOT) { registry.register(module); } @@ -349,8 +374,6 @@ trait Submodules<'heap> { fn names() -> impl IntoIterator>; - fn register(cursor: &mut usize, items: &mut [any::TypeId]); - fn modules( context: &mut StandardLibraryContext<'_, 'heap, S>, cache: &mut ModuleCache<'heap, S>, @@ -367,8 +390,6 @@ impl<'heap> Submodules<'heap> for () { iter::empty() } - fn register(_: &mut usize, _: &mut [any::TypeId]) {} - fn modules( _: &mut StandardLibraryContext<'_, 'heap, S>, _: &mut ModuleCache<'heap, S>, @@ -400,15 +421,6 @@ macro_rules! impl_submodules { const LENGTH: usize = ${count($item)}; const ITEMS: usize = ${count($item)} $(+ <$item::Children as Submodules<'heap>>::ITEMS)*; - fn register(cursor: &mut usize, items: &mut [any::TypeId]) { - $( - items[*cursor] = any::TypeId::of::<$item>(); - *cursor += 1; - - <$item::Children as Submodules<'heap>>::register(cursor, items); - )* - } - fn names() -> impl IntoIterator> { $(let $item = $item::name();)* @@ -432,6 +444,12 @@ macro_rules! impl_submodules { impl_submodules!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P); trait StandardLibraryModule<'heap>: 'static { + /// Unique index for this module in the `ModuleCache` entries array. + /// + /// Assigned in preorder traversal of the module tree (parent before children, left before + /// right). IDs must be unique across all stdlib modules and less than 64. + const CACHE_ID: CacheId; + type Children: Submodules<'heap>; fn name() -> Symbol<'heap>; @@ -475,7 +493,11 @@ macro_rules! decl { TypeDef { id: closure, - arguments: $lib.ty.env.intern_generic_argument_references(&[$(${concat($generic, _ref)}),*]), + arguments: if ${count($generic)} > 0 { + $lib.ty.env.intern_generic_argument_references(&[$(${concat($generic, _ref)}),*]) + } else { + crate::intern::Interned::empty() + }, } }}; } diff --git a/libs/darwin-kperf/criterion/src/pmc.rs b/libs/darwin-kperf/criterion/src/pmc.rs index 188804f5ba3..1bf4eee824c 100644 --- a/libs/darwin-kperf/criterion/src/pmc.rs +++ b/libs/darwin-kperf/criterion/src/pmc.rs @@ -145,6 +145,9 @@ impl Drop for HardwareCounter { // drop order is field declaration order), and we are the sole owner. // No ThreadSampler will be running after this point. let _result = unsafe { SAMPLER.release() }; + + // The sampler can now be acquired again. + SAMPLER_ACQUIRED.store(false, Ordering::SeqCst); } } From 7aa1ddf9e9ed715ffaabdd53e355f7ccdac567c0 Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud <7252775+indietyp@users.noreply.github.com> Date: Fri, 19 Jun 2026 16:29:48 +0200 Subject: [PATCH 07/12] chore: remove any locking concerns from the module system --- libs/@local/hashql/core/src/lib.rs | 1 + libs/@local/hashql/core/src/module/item.rs | 4 +- libs/@local/hashql/core/src/module/mod.rs | 225 +++++++++--------- .../hashql/core/src/module/namespace.rs | 12 +- .../@local/hashql/core/src/module/resolver.rs | 12 +- .../hashql/core/src/module/std_lib/mod.rs | 152 ++++++------ 6 files changed, 205 insertions(+), 201 deletions(-) diff --git a/libs/@local/hashql/core/src/lib.rs b/libs/@local/hashql/core/src/lib.rs index 11ea5e65acc..347982b6fc7 100644 --- a/libs/@local/hashql/core/src/lib.rs +++ b/libs/@local/hashql/core/src/lib.rs @@ -39,6 +39,7 @@ sync_nonpoison, try_trait_v2, variant_count, + iter_collect_into )] extern crate alloc; diff --git a/libs/@local/hashql/core/src/module/item.rs b/libs/@local/hashql/core/src/module/item.rs index bb4906df8e3..5fb363ce96f 100644 --- a/libs/@local/hashql/core/src/module/item.rs +++ b/libs/@local/hashql/core/src/module/item.rs @@ -87,7 +87,7 @@ impl<'heap> Item<'heap> { return None; } - let module = registry.modules.index(next); + let module = registry.modules[next]; next = module.parent; Some(module) @@ -95,6 +95,7 @@ impl<'heap> Item<'heap> { } // TODO: deprecate + #[must_use] pub fn absolute_path( &self, registry: &ModuleRegistry<'heap>, @@ -108,6 +109,7 @@ impl<'heap> Item<'heap> { .chain(iter::once(self.name)) } + #[must_use] pub fn absolute_path_rev( &self, registry: &ModuleRegistry<'heap>, diff --git a/libs/@local/hashql/core/src/module/mod.rs b/libs/@local/hashql/core/src/module/mod.rs index 48e66fcc216..e9dff125ec2 100644 --- a/libs/@local/hashql/core/src/module/mod.rs +++ b/libs/@local/hashql/core/src/module/mod.rs @@ -12,11 +12,7 @@ mod resolver; pub mod std_lib; pub mod universe; -use core::{num::NonZero, slice}; -use std::{ - alloc::{Allocator, Global}, - sync::RwLock, -}; +use core::{alloc::Allocator, num::NonZero, slice}; use self::{ error::{ResolutionError, ResolutionSuggestion}, @@ -26,10 +22,10 @@ use self::{ }; pub use self::{resolver::Reference, universe::Universe}; use crate::{ - collections::{FastHashMap, FastHashSet}, - heap::Heap, - id::{HasId, Id as _, newtype}, - intern::{Decompose, InternMap, InternSet, Interned, Provisioned}, + collections::{FastHashMap, fast_hash_map_in}, + heap::{BumpAllocator as _, Heap}, + id::{HasId, Id as _, IdSlice, IdVec, bit_vec::DenseBitSet, newtype}, + intern::{Decompose, InternSet, Interned}, symbol::Symbol, r#type::environment::Environment, }; @@ -43,52 +39,24 @@ impl ModuleId { pub const ROOT: Self = Self::MAX; } -/// The central registry for all modules and items in a HashQL program. -/// -/// The `ModuleRegistry` serves as the global namespace for module resolution. -/// It tracks all available modules and their exported items. -#[derive(Debug)] -pub struct ModuleRegistry<'heap> { - /// A reference to the global heap used for memory allocation. - pub heap: &'heap Heap, - - pub modules: InternMap<'heap, Module<'heap>>, +pub struct PartialModuleRegistry<'heap, S: Allocator> { + modules: IdVec>, S>, items: InternSet<'heap, [Item<'heap>]>, - root: RwLock, ModuleId>>, + root: FastHashMap, ModuleId, &'heap Heap>, } -impl<'heap> ModuleRegistry<'heap> { - /// Creates an empty module registry using the given heap. - pub fn empty(heap: &'heap Heap) -> Self { +impl<'heap, S: Allocator> PartialModuleRegistry<'heap, S> { + pub fn new_in(heap: &'heap Heap, scratch: S) -> Self { Self { - heap, - modules: InternMap::new(heap), + modules: IdVec::new_in(scratch), items: InternSet::new(heap), - root: RwLock::default(), + root: fast_hash_map_in(heap), } } - /// Creates a new module registry with the standard library pre-loaded. - /// - /// This initializes the registry with all the standard modules and items - /// defined in the standard library. - pub fn new(env: &Environment<'heap>) -> Self { - let this = Self::empty(env.heap); - - let mut std = StandardLibrary::new(env, &this, Global); - std.register(); - - this - } - - pub fn new_in(env: &Environment<'heap>, alloc: S) -> Self { - let this = Self::empty(env.heap); - - let mut std = StandardLibrary::new(env, &this, alloc); - std.register(); - - this + pub fn provision_module(&mut self) -> ModuleId { + self.modules.push(None) } /// Interns a new module into the registry. @@ -97,32 +65,28 @@ impl<'heap> ModuleRegistry<'heap> { /// /// In debug builds, this function will panic if any item in the module has a parent /// that doesn't match the module ID. - pub fn intern_module( - &self, - closure: impl FnOnce(Provisioned) -> PartialModule<'heap>, - ) -> ModuleId { - self.modules - .intern(|id| { - let module = closure(id); - - if cfg!(debug_assertions) { - for item in module.items { - assert_eq!(item.module, id.value()); - - // check for modules if the parent is also set *correctly* to our module - if let ItemKind::Module(child) = item.kind { - let child = self.modules.index(child); - - assert_eq!(child.parent, id.value()); - assert_eq!(child.depth.get(), module.depth.get() + 1); - assert_eq!(child.name, item.name); - } - } + pub fn insert_module(&mut self, module: Module<'heap>) { + #[cfg(debug_assertions)] + { + for item in module.items { + assert_eq!(item.module, module.id); + + // check for modules if the parent is also set *correctly* to our module + if let ItemKind::Module(child) = item.kind { + let child = self + .modules + .lookup(child) + .expect("child modules should be registered before their parents"); + + assert_eq!(child.parent, module.id); + assert_eq!(child.depth.get(), module.depth.get() + 1); + assert_eq!(child.name, item.name); } + } + } - module - }) - .id + let value = self.modules.insert(module.id, module); + debug_assert!(value.is_none()); } /// Interns a slice of items into the registry. @@ -135,16 +99,66 @@ impl<'heap> ModuleRegistry<'heap> { /// # Panics /// /// This function will panic if the internal `RwLock` is poisoned. - pub fn register(&self, module: ModuleId) { - let module = self.modules.index(module); + pub fn register(&mut self, module: ModuleId) { + let module = self + .modules + .lookup(module) + .expect("module must be inserted to be able to register it"); + + debug_assert_eq!(module.parent, ModuleId::ROOT); + + self.root.insert(module.name, module.id); + } + + #[expect(unsafe_code)] + pub fn finish(self, heap: &'heap Heap) -> ModuleRegistry<'heap> { + assert!( + self.modules.iter().all(Option::is_some), + "all modules must be inserted to be able to finish the registry" + ); + + let modules = heap.allocate_slice_uninit(self.modules.len()); + for (dst, src) in modules.iter_mut().zip(self.modules.iter()) { + // SAFETY: We have just verified above that all modules are Some + unsafe { + dst.write(src.unwrap_unchecked()); + } + } - if cfg!(debug_assertions) { - assert_eq!(module.parent, ModuleId::ROOT); + // SAFETY: We have just written all items into the slice, and have verified above that all + // modules are Some + let modules = unsafe { modules.assume_init_ref() }; + + ModuleRegistry { + heap, + modules: IdSlice::from_raw(modules), + root: self.root, } + } +} + +/// The central registry for all modules and items in a HashQL program. +/// +/// The `ModuleRegistry` serves as the global namespace for module resolution. +/// It tracks all available modules and their exported items. +#[derive(Debug)] +pub struct ModuleRegistry<'heap> { + /// A reference to the global heap used for memory allocation. + pub heap: &'heap Heap, + + modules: &'heap IdSlice>, - let mut root = self.root.write().expect("lock should not be poisoned"); - root.insert(module.name, module.id); - drop(root); + root: FastHashMap, ModuleId, &'heap Heap>, +} + +impl<'heap> ModuleRegistry<'heap> { + pub fn new_in(env: &Environment<'heap>, scratch: S) -> Self { + let mut partial = PartialModuleRegistry::new_in(env.heap, scratch.clone()); + + let mut std = StandardLibrary::new(env, &mut partial, scratch); + std.register(); + + partial.finish(env.heap) } /// Find an item by name in the root namespace. @@ -152,31 +166,18 @@ impl<'heap> ModuleRegistry<'heap> { /// # Panics /// /// This function will panic if the internal `RwLock` is poisoned. + #[must_use] pub fn find_by_name(&self, name: Symbol<'heap>) -> Option> { - let root = self.root.read().expect("lock should not be poisoned"); - - let id = root.get(&name).copied()?; - drop(root); - - let module = self.modules.index(id); + let id = self.root.get(&name).copied()?; - Some(module) + Some(self.modules[id]) } /// Finds suggestions for the given name in the root namespace. - fn suggestions(&self) -> Vec> { - let root = self.root.read().expect("lock should not be poisoned"); - - let mut results = Vec::with_capacity(root.len()); - for (&key, &module) in &*root { - results.push(ResolutionSuggestion { - item: module, - name: key, - }); - } - drop(root); - - results + fn suggestions(&self) -> impl ExactSizeIterator> { + self.root + .iter() + .map(|(&name, &id)| ResolutionSuggestion { item: id, name }) } /// Resolves a path to an item in the registry. @@ -293,23 +294,15 @@ impl<'heap> ModuleRegistry<'heap> { /// # Panics /// /// This function will panic if the internal `RwLock` is poisoned. + #[must_use] pub fn search_by_name( &self, name: Symbol<'heap>, universe: Universe, ) -> impl IntoIterator> { - let mut stack: Vec<_> = self - .root - .read() - .expect("lock should not be poisoned") - .values() - .copied() - .collect(); - - let mut seen = FastHashSet::with_capacity_and_hasher( - stack.len(), - foldhash::fast::RandomState::default(), - ); + let mut stack: Vec<_> = self.root.values().copied().collect(); + + let mut seen = DenseBitSet::new_empty(self.modules.len()); let mut current: slice::Iter<'heap, Item<'heap>> = [].iter(); core::iter::from_fn(move || { @@ -321,7 +314,7 @@ impl<'heap> ModuleRegistry<'heap> { } if let ItemKind::Module(child) = item.kind - && !seen.contains(&child) + && !seen.contains(child) { stack.push(child); } @@ -329,14 +322,11 @@ impl<'heap> ModuleRegistry<'heap> { // Current module is exhausted, try to get the next module from the stack while let Some(id) = stack.pop() { - if seen.contains(&id) { + if !seen.insert(id) { continue; } - seen.insert(id); - - let module = self.modules.index(id); - current = module.items.into_iter(); + current = self.modules[id].items.iter(); // Jump back to processing items in this new module continue 'outer; @@ -350,14 +340,13 @@ impl<'heap> ModuleRegistry<'heap> { }) } + #[must_use] pub fn module_depth(&self, id: ModuleId) -> u32 { if id == ModuleId::ROOT { return 0; } - let module = self.modules.index(id); - - module.depth.get() + self.modules[id].depth.get() } } diff --git a/libs/@local/hashql/core/src/module/namespace.rs b/libs/@local/hashql/core/src/module/namespace.rs index 072e0a91a35..b6f745c854b 100644 --- a/libs/@local/hashql/core/src/module/namespace.rs +++ b/libs/@local/hashql/core/src/module/namespace.rs @@ -312,13 +312,9 @@ impl<'env, 'heap> ModuleNamespace<'env, 'heap> { } fn import_modules(&mut self) { - let root = self - .registry - .root - .read() - .expect("should be able to lock registry"); + let root = &self.registry.root; - for (&name, &module) in &*root { + for (&name, &module) in root { self.imports.push(Import { name, item: ImportReference::Item(Item { @@ -688,7 +684,7 @@ mod tests { let mut namespace = ModuleNamespace::new(®istry); namespace.import_prelude(); - let module = registry.intern_module(|id| PartialModule { + let module = registry.insert_module(|id| PartialModule { parent: ModuleId::ROOT, depth: const { NonZero::new(1).unwrap() }, name: heap.intern_symbol("foo"), @@ -981,7 +977,7 @@ mod tests { panic!("expected intrinsic value item"); }; - let custom_module = registry.intern_module(|id| PartialModule { + let custom_module = registry.insert_module(|id| PartialModule { parent: ModuleId::ROOT, depth: const { NonZero::new(1).unwrap() }, name: heap.intern_symbol("custom"), diff --git a/libs/@local/hashql/core/src/module/resolver.rs b/libs/@local/hashql/core/src/module/resolver.rs index 1dd95181553..083310b878b 100644 --- a/libs/@local/hashql/core/src/module/resolver.rs +++ b/libs/@local/hashql/core/src/module/resolver.rs @@ -200,7 +200,7 @@ impl<'heap> Resolver<'_, 'heap> { }); }; - let module = self.registry.modules.index(module); + let module = self.registry.modules[module]; if module.items.is_empty() { return Err(ResolutionError::ModuleEmpty { depth }); @@ -261,7 +261,7 @@ impl<'heap> Resolver<'_, 'heap> { }); }; - module = self.registry.modules.index(next); + module = self.registry.modules[next]; }; match self.options.mode { @@ -291,7 +291,7 @@ impl<'heap> Resolver<'_, 'heap> { return Err(ResolutionError::PackageNotFound { depth: 0, name, - suggestions: self.suggest(|| self.registry.suggestions()), + suggestions: self.suggest(|| self.registry.suggestions().collect()), }); }; @@ -444,7 +444,7 @@ impl<'heap> Resolver<'_, 'heap> { }); }; - let module = self.registry.modules.index(module); + let module = self.registry.modules[module]; if module.items.is_empty() { return Err(ResolutionError::ModuleEmpty { depth: 0 }); @@ -503,7 +503,7 @@ impl<'heap> Resolver<'_, 'heap> { }); }; - let module = self.registry.modules.index(module); + let module = self.registry.modules[module]; return self.resolve_impl(query, module); } @@ -892,7 +892,7 @@ mod test { let registry = ModuleRegistry::new(&env); // Create an empty module and register it - let empty_module_id = registry.intern_module(|_| { + let empty_module_id = registry.insert_module(|_| { PartialModule { name: heap.intern_symbol("empty_module"), parent: ModuleId::ROOT, diff --git a/libs/@local/hashql/core/src/module/std_lib/mod.rs b/libs/@local/hashql/core/src/module/std_lib/mod.rs index d288ef08b05..cbb755c3ca4 100644 --- a/libs/@local/hashql/core/src/module/std_lib/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/mod.rs @@ -4,13 +4,10 @@ mod kernel; use ::core::{alloc::Allocator, iter, mem, mem::MaybeUninit, num::NonZero}; -use super::{ModuleId, ModuleRegistry, item::IntrinsicItem, locals::TypeDef}; +use super::{Module, ModuleId, PartialModuleRegistry, item::IntrinsicItem, locals::TypeDef}; use crate::{ id::{Id as _, bit_vec::FiniteBitSet}, - module::{ - PartialModule, - item::{ConstructorItem, Item, ItemKind}, - }, + module::item::{ConstructorItem, Item, ItemKind}, symbol::Symbol, r#type::{ TypeBuilder, TypeId, @@ -147,7 +144,7 @@ impl<'heap, S: Allocator> ModuleDef<'heap, S> { } } -impl<'heap> ItemDef<'heap> { +impl ItemDef<'_> { /// Returns the number of items emitted when building this definition. /// /// `Newtype` emits both a constructor and a type; other variants emit one item. @@ -199,7 +196,7 @@ enum CacheId { struct StandardLibraryContext<'env, 'heap, S: Allocator> { pub instantiate: InstantiateEnvironment<'env, 'heap>, - pub registry: &'env ModuleRegistry<'heap>, + pub registry: &'env mut PartialModuleRegistry<'heap, S>, pub ty: TypeBuilder<'env, 'heap>, pub alloc: S, } @@ -215,10 +212,10 @@ struct ModuleCache<'heap, S: Allocator> { #[expect(unsafe_code)] impl<'heap, S: Allocator> ModuleCache<'heap, S> { - fn new_in(count: usize, alloc: S) -> Self { + fn new_in(count: u32, alloc: S) -> Self { Self { - entries: Box::new_uninit_slice_in(count, alloc), - occupied: FiniteBitSet::new_empty(count as u32), + entries: Box::new_uninit_slice_in(count as usize, alloc), + occupied: FiniteBitSet::new_empty(count), } } @@ -252,63 +249,70 @@ impl<'heap, S: Allocator> ModuleCache<'heap, S> { T: StandardLibraryModule<'heap>, S: Clone, { - context.registry.intern_module(|id| { - let def = self.request::(context); - let mut output = - Vec::with_capacity_in(def.emitted_len + T::Children::LENGTH, context.alloc.clone()); - let module = id.value(); - - for &ModuleEntry { name, def } in &def.entries { - match def { - ItemDef::Intrinsic(intrinsic) => { - output.push(Item { - module, - name, - kind: ItemKind::Intrinsic(intrinsic), - }); - } - ItemDef::Type(typedef) => { - output.push(Item { - module, - name, - kind: ItemKind::Type(typedef), - }); - } - ItemDef::Newtype(typedef) => { - output.push(Item { - module, - name, - kind: ItemKind::Constructor(ConstructorItem { r#type: typedef }), - }); - output.push(Item { - module, - name, - kind: ItemKind::Type(typedef), - }); - } + let module = context.registry.provision_module(); + let emitted = self.request::(context).emitted_len; + + let mut output = + Vec::with_capacity_in(emitted + T::Children::LENGTH, context.alloc.clone()); + + T::Children::names() + .into_iter() + .zip(T::Children::modules( + context, + self, + depth.saturating_add(1), + module, + )) + .map(|(name, child)| Item { + module, + name, + kind: ItemKind::Module(child), + }) + .collect_into(&mut output); + + let def = self.request::(context); + + for &ModuleEntry { name, def } in &def.entries { + match def { + ItemDef::Intrinsic(intrinsic) => { + output.push(Item { + module, + name, + kind: ItemKind::Intrinsic(intrinsic), + }); + } + ItemDef::Type(typedef) => { + output.push(Item { + module, + name, + kind: ItemKind::Type(typedef), + }); + } + ItemDef::Newtype(typedef) => { + output.push(Item { + module, + name, + kind: ItemKind::Constructor(ConstructorItem { r#type: typedef }), + }); + output.push(Item { + module, + name, + kind: ItemKind::Type(typedef), + }); } } + } - // create all the child modules - let children_names = T::Children::names(); - let children_modules = - T::Children::modules(context, self, depth.saturating_add(1), module); - - for (name, child) in children_names.into_iter().zip(children_modules) { - output.push(Item { - module, - name, - kind: ItemKind::Module(child), - }); - } + let module = Module { + id: module, + name: T::name(), + parent, + depth, + items: context.registry.intern_items(&output), + }; - PartialModule { - name: T::name(), - parent, - depth, - items: context.registry.intern_items(&output), - } - }) + context.registry.insert_module(module); + module.id } } @@ -331,7 +335,7 @@ pub(super) struct StandardLibrary<'env, 'heap, S: Allocator> { impl<'env, 'heap, S: Allocator> StandardLibrary<'env, 'heap, S> { pub(super) fn new( environment: &'env Environment<'heap>, - registry: &'env ModuleRegistry<'heap>, + registry: &'env mut PartialModuleRegistry<'heap, S>, alloc: S, ) -> Self { Self { @@ -344,6 +348,10 @@ impl<'env, 'heap, S: Allocator> StandardLibrary<'env, 'heap, S> { } } + #[expect( + clippy::cast_possible_truncation, + reason = "module count is less than u32::MAX" + )] pub(super) fn register(&mut self) where S: Clone, @@ -359,11 +367,19 @@ impl<'env, 'heap, S: Allocator> StandardLibrary<'env, 'heap, S> { ); let alloc = self.context.alloc.clone(); - let registry = self.context.registry; - let mut cache = ModuleCache::new_in(MODULE_COUNT, alloc); - for module in Root::modules(&mut self.context, &mut cache, ONE, ModuleId::ROOT) { - registry.register(module); + let mut cache = ModuleCache::new_in(MODULE_COUNT as u32, alloc); + + let mut output = [ModuleId::ROOT; Root::LENGTH]; + for (module, output) in Root::modules(&mut self.context, &mut cache, ONE, ModuleId::ROOT) + .into_iter() + .zip(&mut output) + { + *output = module; + } + + for module in output { + self.context.registry.register(module); } } } From 54a68a32510a1afff392fae42c900877e028926c Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud <7252775+indietyp@users.noreply.github.com> Date: Fri, 19 Jun 2026 16:54:34 +0200 Subject: [PATCH 08/12] chore: remove any locking concerns from the module system --- libs/@local/hashql/core/src/lib.rs | 3 +- libs/@local/hashql/core/src/module/item.rs | 1 + libs/@local/hashql/core/src/module/mod.rs | 34 +-------- .../hashql/core/src/module/std_lib/mod.rs | 71 +++++++++++-------- 4 files changed, 47 insertions(+), 62 deletions(-) diff --git a/libs/@local/hashql/core/src/lib.rs b/libs/@local/hashql/core/src/lib.rs index 347982b6fc7..7c9f492c957 100644 --- a/libs/@local/hashql/core/src/lib.rs +++ b/libs/@local/hashql/core/src/lib.rs @@ -39,7 +39,8 @@ sync_nonpoison, try_trait_v2, variant_count, - iter_collect_into + iter_collect_into, + maybe_uninit_fill )] extern crate alloc; diff --git a/libs/@local/hashql/core/src/module/item.rs b/libs/@local/hashql/core/src/module/item.rs index 5fb363ce96f..56b2f2c85f3 100644 --- a/libs/@local/hashql/core/src/module/item.rs +++ b/libs/@local/hashql/core/src/module/item.rs @@ -76,6 +76,7 @@ pub struct Item<'heap> { } impl<'heap> Item<'heap> { + #[must_use] pub fn ancestors( &self, registry: &ModuleRegistry<'heap>, diff --git a/libs/@local/hashql/core/src/module/mod.rs b/libs/@local/hashql/core/src/module/mod.rs index e9dff125ec2..e431514a2fd 100644 --- a/libs/@local/hashql/core/src/module/mod.rs +++ b/libs/@local/hashql/core/src/module/mod.rs @@ -25,7 +25,6 @@ use crate::{ collections::{FastHashMap, fast_hash_map_in}, heap::{BumpAllocator as _, Heap}, id::{HasId, Id as _, IdSlice, IdVec, bit_vec::DenseBitSet, newtype}, - intern::{Decompose, InternSet, Interned}, symbol::Symbol, r#type::environment::Environment, }; @@ -40,8 +39,8 @@ impl ModuleId { } pub struct PartialModuleRegistry<'heap, S: Allocator> { + heap: &'heap Heap, modules: IdVec>, S>, - items: InternSet<'heap, [Item<'heap>]>, root: FastHashMap, ModuleId, &'heap Heap>, } @@ -49,8 +48,8 @@ pub struct PartialModuleRegistry<'heap, S: Allocator> { impl<'heap, S: Allocator> PartialModuleRegistry<'heap, S> { pub fn new_in(heap: &'heap Heap, scratch: S) -> Self { Self { + heap, modules: IdVec::new_in(scratch), - items: InternSet::new(heap), root: fast_hash_map_in(heap), } } @@ -89,11 +88,6 @@ impl<'heap, S: Allocator> PartialModuleRegistry<'heap, S> { debug_assert!(value.is_none()); } - /// Interns a slice of items into the registry. - pub fn intern_items(&self, items: &[Item<'heap>]) -> Interned<'heap, [Item<'heap>]> { - self.items.intern_slice(items) - } - /// Register a new module in the root namespace. /// /// # Panics @@ -363,7 +357,7 @@ pub struct Module<'heap> { pub parent: ModuleId, pub depth: NonZero, - pub items: Interned<'heap, [Item<'heap>]>, + pub items: &'heap [Item<'heap>], } impl<'heap> Module<'heap> { @@ -388,28 +382,6 @@ impl<'heap> Module<'heap> { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct PartialModule<'heap> { - name: Symbol<'heap>, - parent: ModuleId, - depth: NonZero, - items: Interned<'heap, [Item<'heap>]>, -} - -impl<'heap> Decompose<'heap> for Module<'heap> { - type Partial = PartialModule<'heap>; - - fn from_parts(id: Self::Id, partial: Interned<'heap, Self::Partial>) -> Self { - Self { - id, - name: partial.name, - parent: partial.parent, - depth: partial.depth, - items: partial.items, - } - } -} - impl HasId for Module<'_> { type Id = ModuleId; diff --git a/libs/@local/hashql/core/src/module/std_lib/mod.rs b/libs/@local/hashql/core/src/module/std_lib/mod.rs index cbb755c3ca4..f1c3f4198ec 100644 --- a/libs/@local/hashql/core/src/module/std_lib/mod.rs +++ b/libs/@local/hashql/core/src/module/std_lib/mod.rs @@ -6,6 +6,7 @@ use ::core::{alloc::Allocator, iter, mem, mem::MaybeUninit, num::NonZero}; use super::{Module, ModuleId, PartialModuleRegistry, item::IntrinsicItem, locals::TypeDef}; use crate::{ + heap::BumpAllocator as _, id::{Id as _, bit_vec::FiniteBitSet}, module::item::{ConstructorItem, Item, ItemKind}, symbol::Symbol, @@ -249,66 +250,76 @@ impl<'heap, S: Allocator> ModuleCache<'heap, S> { T: StandardLibraryModule<'heap>, S: Clone, { - let module = context.registry.provision_module(); - let emitted = self.request::(context).emitted_len; - - let mut output = - Vec::with_capacity_in(emitted + T::Children::LENGTH, context.alloc.clone()); - - T::Children::names() - .into_iter() - .zip(T::Children::modules( - context, - self, - depth.saturating_add(1), - module, - )) - .map(|(name, child)| Item { - module, - name, - kind: ItemKind::Module(child), - }) - .collect_into(&mut output); + let id = context.registry.provision_module(); let def = self.request::(context); + let heap = context.registry.heap; + let output = heap.allocate_slice_uninit(def.emitted_len + T::Children::LENGTH); + + let mut cursor = 0; for &ModuleEntry { name, def } in &def.entries { match def { ItemDef::Intrinsic(intrinsic) => { - output.push(Item { - module, + output[cursor].write(Item { + module: id, name, kind: ItemKind::Intrinsic(intrinsic), }); + cursor += 1; } ItemDef::Type(typedef) => { - output.push(Item { - module, + output[cursor].write(Item { + module: id, name, kind: ItemKind::Type(typedef), }); + cursor += 1; } ItemDef::Newtype(typedef) => { - output.push(Item { - module, + output[cursor].write(Item { + module: id, name, kind: ItemKind::Constructor(ConstructorItem { r#type: typedef }), }); - output.push(Item { - module, + cursor += 1; + + output[cursor].write(Item { + module: id, name, kind: ItemKind::Type(typedef), }); + cursor += 1; } } } + let remaining = &mut output[cursor..]; + let (_, remaining) = remaining.write_iter( + T::Children::names() + .into_iter() + .zip(T::Children::modules( + context, + self, + depth.saturating_add(1), + id, + )) + .map(|(name, child)| Item { + module: id, + name, + kind: ItemKind::Module(child), + }), + ); + + assert!(remaining.is_empty()); + let module = Module { - id: module, + id, name: T::name(), parent, depth, - items: context.registry.intern_items(&output), + // SAFETY: assertion ensures that the output buffer is fully initialized before reading. + items: unsafe { output.assume_init_ref() }, }; context.registry.insert_module(module); From b34e1ecb9a664993debe3796495de0f24480c4fc Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud <7252775+indietyp@users.noreply.github.com> Date: Fri, 19 Jun 2026 17:07:11 +0200 Subject: [PATCH 09/12] chore: docs --- libs/@local/hashql/core/Cargo.toml | 4 - libs/@local/hashql/core/benches/stdlib.rs | 99 ----------------- libs/@local/hashql/core/src/lib.rs | 1 - libs/@local/hashql/core/src/module/mod.rs | 104 ++++++++++++++++-- .../hashql/core/src/module/namespace.rs | 79 ++++++------- .../@local/hashql/core/src/module/resolver.rs | 27 ++--- 6 files changed, 134 insertions(+), 180 deletions(-) delete mode 100644 libs/@local/hashql/core/benches/stdlib.rs diff --git a/libs/@local/hashql/core/Cargo.toml b/libs/@local/hashql/core/Cargo.toml index e51611ca379..8a6b5b68221 100644 --- a/libs/@local/hashql/core/Cargo.toml +++ b/libs/@local/hashql/core/Cargo.toml @@ -64,7 +64,3 @@ harness = false [[bench]] name = "bit_matrix" harness = false - -[[bench]] -name = "stdlib" -harness = false diff --git a/libs/@local/hashql/core/benches/stdlib.rs b/libs/@local/hashql/core/benches/stdlib.rs deleted file mode 100644 index 90a8704dc93..00000000000 --- a/libs/@local/hashql/core/benches/stdlib.rs +++ /dev/null @@ -1,99 +0,0 @@ -#![expect(clippy::significant_drop_tightening)] - -use core::{hint::black_box, time::Duration}; - -use codspeed_criterion_compat::{ - BatchSize, Criterion, criterion_group, criterion_main, measurement::Measurement, -}; -use darwin_kperf_criterion::HardwareCounter; -use hashql_core::{ - heap::{Heap, ResetAllocator as _}, - module::ModuleRegistry, - r#type::environment::Environment, -}; - -fn stdlib_construction(criterion: &mut Criterion) { - let mut group = criterion.benchmark_group("stdlib"); - - group.bench_function("construction", |bencher| { - let mut heap = Heap::new(); - let heap_ptr = &raw mut heap; - - // IMPORTANT: `BatchSize::PerIteration` is critical for soundness. Do NOT change this to - // `SmallInput`, `LargeInput`, or any other batch size. Doing so will cause undefined - // behavior (use-after-free of arena allocations). - bencher.iter_batched( - || { - // SAFETY: We create a `&mut Heap` from the raw pointer to call `reset()` and - // build the environment. This is sound because: - // - `heap` outlives the entire `iter_batched` call (it's a local in the outer - // scope). - // - `BatchSize::PerIteration` ensures only one environment exists at a time, - // dropped before the next setup call. - // - No other references to `heap` exist during this closure's execution. - // - This code runs single-threaded. - #[expect(unsafe_code)] - let heap = unsafe { &mut *heap_ptr }; - heap.reset(); - - Environment::new(heap) - }, - |environment| { - black_box(ModuleRegistry::new_in(&environment, environment.heap)); - }, - BatchSize::PerIteration, - ); - }); - - group.finish(); -} - -fn measurement(counter: HardwareCounter) -> Criterion { - Criterion::default() - .with_measurement(counter) - .warm_up_time(Duration::from_millis(500)) - .measurement_time(Duration::from_secs(3)) - .sample_size(50) -} - -fn measurement_instructions() -> Criterion { - measurement(HardwareCounter::instructions().expect("failed to initialize hardware counters")) -} - -fn measurement_l1d_cache_misses() -> Criterion { - measurement( - HardwareCounter::l1d_cache_misses().expect("failed to initialize hardware counters"), - ) -} - -fn measurement_branch_misses() -> Criterion { - measurement( - HardwareCounter::branch_mispredictions().expect("failed to initialize hardware counters"), - ) -} - -criterion_group! { - name = walltime; - config = Criterion::default(); - targets = stdlib_construction -} - -criterion_group!( - name = instructions; - config = measurement_instructions(); - targets = stdlib_construction -); - -criterion_group!( - name = l1d_cache_misses; - config = measurement_l1d_cache_misses(); - targets = stdlib_construction -); - -criterion_group!( - name = branch_misses; - config = measurement_branch_misses(); - targets = stdlib_construction -); - -criterion_main!(walltime, instructions, l1d_cache_misses, branch_misses); diff --git a/libs/@local/hashql/core/src/lib.rs b/libs/@local/hashql/core/src/lib.rs index 7c9f492c957..daf94f95779 100644 --- a/libs/@local/hashql/core/src/lib.rs +++ b/libs/@local/hashql/core/src/lib.rs @@ -39,7 +39,6 @@ sync_nonpoison, try_trait_v2, variant_count, - iter_collect_into, maybe_uninit_fill )] diff --git a/libs/@local/hashql/core/src/module/mod.rs b/libs/@local/hashql/core/src/module/mod.rs index e431514a2fd..15d63e4c668 100644 --- a/libs/@local/hashql/core/src/module/mod.rs +++ b/libs/@local/hashql/core/src/module/mod.rs @@ -38,6 +38,23 @@ impl ModuleId { pub const ROOT: Self = Self::MAX; } +/// A builder for constructing a [`ModuleRegistry`]. +/// +/// Collects module definitions during construction (e.g. stdlib registration) and produces +/// an immutable [`ModuleRegistry`] via [`finish`]. +/// +/// The typical construction sequence is: +/// 1. Create with [`new_in`] +/// 2. Provision module IDs with [`provision_module`] +/// 3. Insert fully built modules with [`insert_module`] +/// 4. Register root-level modules with [`register`] +/// 5. Finalize into a [`ModuleRegistry`] with [`finish`] +/// +/// [`new_in`]: Self::new_in +/// [`provision_module`]: Self::provision_module +/// [`insert_module`]: Self::insert_module +/// [`register`]: Self::register +/// [`finish`]: Self::finish pub struct PartialModuleRegistry<'heap, S: Allocator> { heap: &'heap Heap, modules: IdVec>, S>, @@ -54,16 +71,22 @@ impl<'heap, S: Allocator> PartialModuleRegistry<'heap, S> { } } + /// Reserves a fresh [`ModuleId`] that can be referenced by child items before the + /// module itself is inserted. pub fn provision_module(&mut self) -> ModuleId { self.modules.push(None) } - /// Interns a new module into the registry. + /// Inserts a fully constructed module into the registry. + /// + /// The module's `id` must have been previously obtained from [`provision_module`]. /// /// # Panics /// - /// In debug builds, this function will panic if any item in the module has a parent - /// that doesn't match the module ID. + /// In debug builds, panics if any item in the module references a different module ID + /// than the one being inserted, or if child module metadata is inconsistent. + /// + /// [`provision_module`]: Self::provision_module pub fn insert_module(&mut self, module: Module<'heap>) { #[cfg(debug_assertions)] { @@ -88,11 +111,13 @@ impl<'heap, S: Allocator> PartialModuleRegistry<'heap, S> { debug_assert!(value.is_none()); } - /// Register a new module in the root namespace. + /// Registers a module in the root namespace, making it accessible by name. /// /// # Panics /// - /// This function will panic if the internal `RwLock` is poisoned. + /// Panics if the module has not been inserted via [`insert_module`]. + /// + /// [`insert_module`]: Self::insert_module pub fn register(&mut self, module: ModuleId) { let module = self .modules @@ -104,6 +129,13 @@ impl<'heap, S: Allocator> PartialModuleRegistry<'heap, S> { self.root.insert(module.name, module.id); } + /// Finalizes the builder into an immutable [`ModuleRegistry`]. + /// + /// # Panics + /// + /// Panics if any provisioned module ID was never populated via [`insert_module`]. + /// + /// [`insert_module`]: Self::insert_module #[expect(unsafe_code)] pub fn finish(self, heap: &'heap Heap) -> ModuleRegistry<'heap> { assert!( @@ -146,6 +178,16 @@ pub struct ModuleRegistry<'heap> { } impl<'heap> ModuleRegistry<'heap> { + /// Creates a new module registry with the standard library pre-loaded. + /// + /// This initializes the registry with all the standard modules and items + /// defined in the standard library, using the global allocator for scratch space. + pub fn new(env: &Environment<'heap>) -> Self { + Self::new_in(env, alloc::alloc::Global) + } + + /// Creates a new module registry with the standard library pre-loaded, using `scratch` + /// for temporary allocations during construction. pub fn new_in(env: &Environment<'heap>, scratch: S) -> Self { let mut partial = PartialModuleRegistry::new_in(env.heap, scratch.clone()); @@ -155,11 +197,7 @@ impl<'heap> ModuleRegistry<'heap> { partial.finish(env.heap) } - /// Find an item by name in the root namespace. - /// - /// # Panics - /// - /// This function will panic if the internal `RwLock` is poisoned. + /// Looks up a root-level module by name. #[must_use] pub fn find_by_name(&self, name: Symbol<'heap>) -> Option> { let id = self.root.get(&name).copied()?; @@ -389,3 +427,49 @@ impl HasId for Module<'_> { self.id } } + +#[cfg(test)] +impl<'heap> ModuleRegistry<'heap> { + /// Creates a partially built registry with the stdlib loaded. + /// + /// Returns a `PartialModuleRegistry` that can be further customized (e.g. by inserting + /// test modules) before calling [`PartialModuleRegistry::finish`]. + pub(crate) fn builder( + env: &Environment<'heap>, + ) -> PartialModuleRegistry<'heap, alloc::alloc::Global> { + use alloc::alloc::Global; + + let mut partial = PartialModuleRegistry::new_in(env.heap, Global); + + let mut std = StandardLibrary::new(env, &mut partial, Global); + std.register(); + + partial + } +} + +#[cfg(test)] +impl<'heap, S: Allocator> PartialModuleRegistry<'heap, S> { + /// Inserts a root-level module with the given name and items. + /// + /// This is a test helper that provisions a module ID, passes it to the `items` closure + /// so items can reference their owning module, then inserts and registers the module. + pub(crate) fn insert_root_module( + &mut self, + name: Symbol<'heap>, + items: impl FnOnce(ModuleId) -> &'heap [Item<'heap>], + ) -> ModuleId { + let id = self.provision_module(); + + self.insert_module(Module { + id, + name, + parent: ModuleId::ROOT, + depth: const { NonZero::new(1).unwrap() }, + items: items(id), + }); + self.register(id); + + id + } +} diff --git a/libs/@local/hashql/core/src/module/namespace.rs b/libs/@local/hashql/core/src/module/namespace.rs index b6f745c854b..d7076c88538 100644 --- a/libs/@local/hashql/core/src/module/namespace.rs +++ b/libs/@local/hashql/core/src/module/namespace.rs @@ -53,6 +53,7 @@ pub struct ModuleNamespace<'env, 'heap> { impl<'env, 'heap> ModuleNamespace<'env, 'heap> { /// Create a new module namespace. + #[must_use] pub fn new(registry: &'env ModuleRegistry<'heap>) -> Self { Self { registry, @@ -324,8 +325,6 @@ impl<'env, 'heap> ModuleNamespace<'env, 'heap> { }), }); } - - drop(root); } /// Imports all standard prelude items and all root modules. @@ -476,13 +475,13 @@ impl<'env, 'heap> ModuleNamespace<'env, 'heap> { #[cfg(test)] mod tests { #![coverage(off)] - use core::{assert_matches, num::NonZero}; + use core::assert_matches; use super::ModuleNamespace; use crate::{ - heap::Heap, + heap::{BumpAllocator as _, Heap}, module::{ - ModuleId, ModuleRegistry, PartialModule, Reference, Universe, + ModuleRegistry, Reference, Universe, error::ResolutionError, item::{IntrinsicItem, IntrinsicTypeItem, IntrinsicValueItem, Item, ItemKind}, namespace::{ImportOptions, ResolutionMode, ResolveOptions}, @@ -679,25 +678,21 @@ mod tests { fn shadowed_import() { let heap = Heap::new(); let environment = Environment::new(&heap); - let registry = ModuleRegistry::new(&environment); - - let mut namespace = ModuleNamespace::new(®istry); - namespace.import_prelude(); - - let module = registry.insert_module(|id| PartialModule { - parent: ModuleId::ROOT, - depth: const { NonZero::new(1).unwrap() }, - name: heap.intern_symbol("foo"), - items: registry.intern_items(&[Item { - module: id.value(), + let mut builder = ModuleRegistry::builder(&environment); + builder.insert_root_module(heap.intern_symbol("foo"), |id| { + heap.allocate_slice_copy(&[Item { + module: id, name: heap.intern_symbol("bar"), kind: ItemKind::Intrinsic(IntrinsicItem::Type(IntrinsicTypeItem { name: heap.intern_symbol("::foo::bar"), })), - }]), + }]) }); - registry.register(module); + let registry = builder.finish(&heap); + + let mut namespace = ModuleNamespace::new(®istry); + namespace.import_prelude(); let import = namespace .resolve_relative( @@ -949,7 +944,22 @@ mod tests { fn alias_shadows_import() { let heap = Heap::new(); let environment = Environment::new(&heap); - let registry = ModuleRegistry::new(&environment); + + let mut builder = ModuleRegistry::builder(&environment); + let custom_module = builder.insert_root_module(heap.intern_symbol("custom"), |id| { + heap.allocate_slice_copy(&[Item { + module: id, + name: heap.intern_symbol("my_add"), + kind: ItemKind::Intrinsic(IntrinsicItem::Value(IntrinsicValueItem { + name: heap.intern_symbol("::custom::my_add"), + r#type: crate::module::locals::TypeDef { + id: crate::r#type::builder::TypeBuilder::synthetic(&environment).never(), + arguments: crate::intern::Interned::empty(), + }, + })), + }]) + }); + let registry = builder.finish(&heap); let mut namespace = ModuleNamespace::new(®istry); namespace.import_prelude(); @@ -968,36 +978,9 @@ mod tests { assert_eq!(original.name.as_str(), "add"); - // Create a custom item and alias `+` to it - let ItemKind::Intrinsic(IntrinsicItem::Value(IntrinsicValueItem { - r#type: original_type, - .. - })) = original.kind - else { - panic!("expected intrinsic value item"); - }; - - let custom_module = registry.insert_module(|id| PartialModule { - parent: ModuleId::ROOT, - depth: const { NonZero::new(1).unwrap() }, - name: heap.intern_symbol("custom"), - - items: registry.intern_items(&[Item { - module: id.value(), - name: heap.intern_symbol("my_add"), - kind: ItemKind::Intrinsic(IntrinsicItem::Value(IntrinsicValueItem { - name: heap.intern_symbol("::custom::my_add"), - r#type: original_type, - })), - }]), - }); - registry.register(custom_module); - - let custom_item = *registry - .modules - .index(custom_module) + let custom_item = *registry.modules[custom_module] .items - .into_iter() + .iter() .next() .expect("module should have one item"); diff --git a/libs/@local/hashql/core/src/module/resolver.rs b/libs/@local/hashql/core/src/module/resolver.rs index 083310b878b..2969964f6cd 100644 --- a/libs/@local/hashql/core/src/module/resolver.rs +++ b/libs/@local/hashql/core/src/module/resolver.rs @@ -152,7 +152,7 @@ impl<'heap> Resolver<'_, 'heap> { ) -> Result, ResolutionError<'heap>> { let mut item = module .items - .into_iter() + .iter() .copied() .filter(move |item| item.name == name) .map(Reference::Item) @@ -206,7 +206,7 @@ impl<'heap> Resolver<'_, 'heap> { return Err(ResolutionError::ModuleEmpty { depth }); } - Ok(module.items.into_iter().copied().map(Reference::Item)) + Ok(module.items.iter().copied().map(Reference::Item)) } #[define_opaque(ModuleItemIterator)] @@ -225,7 +225,7 @@ impl<'heap> Resolver<'_, 'heap> { } return Ok(ResolveIter::Glob( - module.items.into_iter().copied().map(Reference::Item), + module.items.iter().copied().map(Reference::Item), )); } @@ -450,7 +450,7 @@ impl<'heap> Resolver<'_, 'heap> { return Err(ResolutionError::ModuleEmpty { depth: 0 }); } - Ok(module.items.into_iter().copied().map(Reference::Item)) + Ok(module.items.iter().copied().map(Reference::Item)) } #[expect(clippy::panic_in_result_fn, reason = "sanity check")] @@ -526,13 +526,13 @@ impl<'heap> Resolver<'_, 'heap> { #[cfg(test)] mod test { #![coverage(off)] - use core::{assert_matches, num::NonZero}; + use core::assert_matches; use super::{Reference, ResolutionError}; use crate::{ heap::Heap, module::{ - ModuleId, ModuleRegistry, PartialModule, Universe, + ModuleRegistry, Universe, item::Item, namespace::{ImportOptions, ModuleNamespace, ResolutionMode}, resolver::{Resolver, ResolverMode, ResolverOptions}, @@ -889,19 +889,10 @@ mod test { fn module_empty_error() { let heap = Heap::new(); let env = Environment::new(&heap); - let registry = ModuleRegistry::new(&env); - - // Create an empty module and register it - let empty_module_id = registry.insert_module(|_| { - PartialModule { - name: heap.intern_symbol("empty_module"), - parent: ModuleId::ROOT, - depth: const { NonZero::new(1).unwrap() }, - items: registry.intern_items(&[]), // No items - } - }); - registry.register(empty_module_id); + let mut builder = ModuleRegistry::builder(&env); + builder.insert_root_module(heap.intern_symbol("empty_module"), |_| &[]); + let registry = builder.finish(&heap); let resolver = Resolver { registry: ®istry, From cd32499946aad39d78be91107a252f635f532ffe Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud <7252775+indietyp@users.noreply.github.com> Date: Fri, 19 Jun 2026 17:10:40 +0200 Subject: [PATCH 10/12] chore: revert kperf changes --- libs/darwin-kperf/criterion/src/pmc.rs | 3 --- libs/darwin-kperf/events/src/lib.rs | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/libs/darwin-kperf/criterion/src/pmc.rs b/libs/darwin-kperf/criterion/src/pmc.rs index 1bf4eee824c..188804f5ba3 100644 --- a/libs/darwin-kperf/criterion/src/pmc.rs +++ b/libs/darwin-kperf/criterion/src/pmc.rs @@ -145,9 +145,6 @@ impl Drop for HardwareCounter { // drop order is field declaration order), and we are the sole owner. // No ThreadSampler will be running after this point. let _result = unsafe { SAMPLER.release() }; - - // The sampler can now be acquired again. - SAMPLER_ACQUIRED.store(false, Ordering::SeqCst); } } diff --git a/libs/darwin-kperf/events/src/lib.rs b/libs/darwin-kperf/events/src/lib.rs index f8b368ee82d..376d24e782a 100644 --- a/libs/darwin-kperf/events/src/lib.rs +++ b/libs/darwin-kperf/events/src/lib.rs @@ -59,7 +59,7 @@ impl Cpu { b"a15" => Some(Self::M2), b"a16" | b"as1" | b"as2" | b"as3" => Some(Self::M3), b"as4" | b"as4-1" | b"as4-2" => Some(Self::M4), - b"as5" | b"as5-1" | b"as5-2" => Some(Self::M5), + b"as5" | b"as5-2" => Some(Self::M5), _ => None, } } From 6d88dc4888fdb686419c9d0ecd36760e664feffd Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud <7252775+indietyp@users.noreply.github.com> Date: Fri, 19 Jun 2026 17:17:38 +0200 Subject: [PATCH 11/12] chore: remove deprecated code --- .../@local/hashql/ast/src/lower/expander/error.rs | 14 ++++++++++++-- libs/@local/hashql/core/src/module/item.rs | 15 --------------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/libs/@local/hashql/ast/src/lower/expander/error.rs b/libs/@local/hashql/ast/src/lower/expander/error.rs index b6ec20e3eed..0a7ee3b1e7a 100644 --- a/libs/@local/hashql/ast/src/lower/expander/error.rs +++ b/libs/@local/hashql/ast/src/lower/expander/error.rs @@ -358,9 +358,19 @@ impl Display for FormatUserPath<'_, '_> { fn format_absolute_path<'heap>(item: &Item<'heap>, registry: &ModuleRegistry<'heap>) -> String { use core::iter; - let path = item.absolute_path(registry).into_iter().map(Symbol::unwrap); + #[expect( + clippy::needless_collect, + reason = "debugging code + required for reverse" + )] + let path: Vec<_> = item + .absolute_path_rev(registry) + .map(Symbol::unwrap) + .collect(); - iter::once("").chain(path).intersperse("::").collect() + iter::once("") + .chain(path.into_iter().rev()) + .intersperse("::") + .collect() } struct SpellingSuggestions<'heap, I> { diff --git a/libs/@local/hashql/core/src/module/item.rs b/libs/@local/hashql/core/src/module/item.rs index 56b2f2c85f3..9bdde1328da 100644 --- a/libs/@local/hashql/core/src/module/item.rs +++ b/libs/@local/hashql/core/src/module/item.rs @@ -95,21 +95,6 @@ impl<'heap> Item<'heap> { }) } - // TODO: deprecate - #[must_use] - pub fn absolute_path( - &self, - registry: &ModuleRegistry<'heap>, - ) -> impl IntoIterator> { - let mut modules: Vec<_> = self.ancestors(registry).into_iter().collect(); - modules.reverse(); - - modules - .into_iter() - .map(|module| module.name) - .chain(iter::once(self.name)) - } - #[must_use] pub fn absolute_path_rev( &self, From df8bde27bd7a4769d09399a058d1c03b3a0a9a42 Mon Sep 17 00:00:00 2001 From: Bilal Mahmoud <7252775+indietyp@users.noreply.github.com> Date: Fri, 19 Jun 2026 17:24:11 +0200 Subject: [PATCH 12/12] fix: remove superfluos heap argument --- libs/@local/hashql/core/src/module/mod.rs | 12 ++++-------- libs/@local/hashql/core/src/module/namespace.rs | 4 ++-- libs/@local/hashql/core/src/module/resolver.rs | 2 +- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/libs/@local/hashql/core/src/module/mod.rs b/libs/@local/hashql/core/src/module/mod.rs index 15d63e4c668..987595f55f9 100644 --- a/libs/@local/hashql/core/src/module/mod.rs +++ b/libs/@local/hashql/core/src/module/mod.rs @@ -137,13 +137,13 @@ impl<'heap, S: Allocator> PartialModuleRegistry<'heap, S> { /// /// [`insert_module`]: Self::insert_module #[expect(unsafe_code)] - pub fn finish(self, heap: &'heap Heap) -> ModuleRegistry<'heap> { + pub fn finish(self) -> ModuleRegistry<'heap> { assert!( self.modules.iter().all(Option::is_some), "all modules must be inserted to be able to finish the registry" ); - let modules = heap.allocate_slice_uninit(self.modules.len()); + let modules = self.heap.allocate_slice_uninit(self.modules.len()); for (dst, src) in modules.iter_mut().zip(self.modules.iter()) { // SAFETY: We have just verified above that all modules are Some unsafe { @@ -156,7 +156,7 @@ impl<'heap, S: Allocator> PartialModuleRegistry<'heap, S> { let modules = unsafe { modules.assume_init_ref() }; ModuleRegistry { - heap, + heap: self.heap, modules: IdSlice::from_raw(modules), root: self.root, } @@ -194,7 +194,7 @@ impl<'heap> ModuleRegistry<'heap> { let mut std = StandardLibrary::new(env, &mut partial, scratch); std.register(); - partial.finish(env.heap) + partial.finish() } /// Looks up a root-level module by name. @@ -322,10 +322,6 @@ impl<'heap> ModuleRegistry<'heap> { /// This method requires allocation for the traversal state: /// - A vector for the module exploration stack /// - A hash set for tracking visited modules - /// - /// # Panics - /// - /// This function will panic if the internal `RwLock` is poisoned. #[must_use] pub fn search_by_name( &self, diff --git a/libs/@local/hashql/core/src/module/namespace.rs b/libs/@local/hashql/core/src/module/namespace.rs index d7076c88538..68467edb9a9 100644 --- a/libs/@local/hashql/core/src/module/namespace.rs +++ b/libs/@local/hashql/core/src/module/namespace.rs @@ -689,7 +689,7 @@ mod tests { })), }]) }); - let registry = builder.finish(&heap); + let registry = builder.finish(); let mut namespace = ModuleNamespace::new(®istry); namespace.import_prelude(); @@ -959,7 +959,7 @@ mod tests { })), }]) }); - let registry = builder.finish(&heap); + let registry = builder.finish(); let mut namespace = ModuleNamespace::new(®istry); namespace.import_prelude(); diff --git a/libs/@local/hashql/core/src/module/resolver.rs b/libs/@local/hashql/core/src/module/resolver.rs index 2969964f6cd..7944bf057d0 100644 --- a/libs/@local/hashql/core/src/module/resolver.rs +++ b/libs/@local/hashql/core/src/module/resolver.rs @@ -892,7 +892,7 @@ mod test { let mut builder = ModuleRegistry::builder(&env); builder.insert_root_module(heap.intern_symbol("empty_module"), |_| &[]); - let registry = builder.finish(&heap); + let registry = builder.finish(); let resolver = Resolver { registry: ®istry,